Android Kotlin Fundamentals 0.1:: Room-Datenbank erstellen

Dieses Codelab ist Teil des Android Kotlin Fundamentals-Kurss. Sie profitieren von diesem Kurs, wenn Sie nacheinander die Codelabs durcharbeiten. Alle Kurs-Codelabs finden Sie auf der Landingpage für Kotlin-Grundlagen für Android-Entwickler.

Einführung

Die meisten Apps haben Daten, die gespeichert werden müssen, auch wenn der Nutzer die App schließt. Beispielsweise kann die App eine Playlist, ein Inventar von Spielelementen, Ausgaben und Einkommen, einen Katalog von Konstellationen oder Schlafdaten im Zeitverlauf speichern. In der Regel verwenden Sie eine Datenbank, um persistente Daten zu speichern.

Room ist eine Datenbankbibliothek, die Teil von Android Jetpack ist. Room übernimmt viele der Aufgaben beim Einrichten und Konfigurieren einer Datenbank und ermöglicht Ihrer App, über normale Funktionsaufrufe mit der Datenbank zu interagieren. hinter den Kulissen Room ist eine Abstraktionsebene über einer SQLite-Datenbank. Verwenden Sie die Terminologie von Room und die Abfragesyntax für komplexere Abfragen, da Sie das SQLite-Modell verwenden.

Die folgende Abbildung zeigt, wie die Room-Datenbank in die allgemeine Architektur passt, die in diesem Kurs empfohlen wird.

Was Sie bereits wissen sollten

Sie sollten mit Folgendem vertraut sein:

  • Eine einfache Benutzeroberfläche für eine Android-App erstellen
  • Aktivitäten, Fragmente und Datenansichten verwenden
  • Die Navigation zwischen Fragmenten und die Verwendung von Safe Args (ein Gradle-Plug-in) zur Übertragung von Daten zwischen Fragmenten.
  • Modelle, Fabriken mit Modellen sowie LiveData und die zugehörigen Betrachter ansehen Diese Themen zu Architekturkomponenten werden in einem früheren Codelab in diesem Kurs behandelt.
  • Grundlegendes Verständnis von SQL-Datenbanken und der SQLite-Sprache In SQLite finden Sie eine kurze Übersicht oder Auffrischung.

Lerninhalte

  • Room-Datenbank zum Erstellen von Daten erstellen und mit ihr interagieren
  • So erstellen Sie eine Datenklasse, mit der eine Tabelle in der Datenbank definiert wird.
  • Anleitung zum Verwenden eines Datenzugriffsobjekts (Data Access Object, DAO), um Kotlin-Funktionen SQL-Abfragen zuzuordnen.
  • So testen Sie, ob Ihre Datenbank funktioniert

Aufgaben

  • Erstelle eine Room-Datenbank mit einer Schnittstelle für nächtliche Schlafdaten.
  • Testen Sie die Datenbank mit den bereitgestellten Tests.

In diesem Codelab erstellst du den Datenbankteil einer App, die die Schlafqualität verfolgt. Die App verwendet eine Datenbank, um Schlafdaten zu speichern.

Die App hat zwei Bildschirme, die durch Fragmente dargestellt werden, wie in der folgenden Abbildung dargestellt.

Auf dem ersten Bildschirm auf der linken Seite finden Sie Schaltflächen zum Starten und Beenden der Aufzeichnung. Auf dem Bildschirm werden alle Schlafdaten des Nutzers angezeigt. Mit der Schaltfläche Löschen werden alle Daten gelöscht, die die App für den Nutzer erfasst hat.

Auf dem zweiten Bildschirm rechts wird die Qualität des Schlafs angezeigt. In der App wird die Bewertung in numerischer Form dargestellt. Zu Entwicklungszwecken zeigt die App sowohl die Gesichtssymbole als auch ihre numerischen Entsprechungen an.

Der Ablauf für Nutzer sieht so aus:

  • Der Nutzer öffnet die App und sieht den Schlaftracking-Bildschirm.
  • Der Nutzer tippt auf die Schaltfläche Starten. Damit wird die Startzeit aufgezeichnet und angezeigt. Die Schaltfläche Starten ist deaktiviert und die Schaltfläche Beenden ist aktiviert.
  • Der Nutzer tippt auf die Schaltfläche Beenden. Damit wird die Endzeit erfasst und der Bildschirm für die Schlafqualität geöffnet.
  • Der Nutzer wählt ein Symbol für die Schlafqualität aus. Der Bildschirm wird geschlossen und der Tracking-Bildschirm zeigt die Schlafzeit und die Schlafqualität an. Die Schaltfläche Stopp ist deaktiviert und die Schaltfläche Starten ist aktiviert. Die App ist bereit für eine weitere Nacht.
  • Die Schaltfläche Löschen ist immer dann verfügbar, wenn Daten in der Datenbank vorhanden sind. Wenn der Nutzer auf Löschen tippt, werden alle seine Daten gelöscht. Es wird keine Meldung angezeigt.

Für diese App wird eine vereinfachte Architektur verwendet, die im Folgenden im Kontext der vollständigen Architektur gezeigt wird. Die Anwendung verwendet nur die folgenden Komponenten:

  • UI-Controller
  • Modell und LiveData ansehen
  • Eine Raumdatenbank

Schritt 1: Start-App herunterladen und ausführen

  1. Laden Sie die App TrackMySleepquality-Starter von GitHub herunter.
  2. Erstelle die App und führe sie aus. Die App zeigt die UI für das SleepTrackerFragment-Fragment an, jedoch keine Daten. Die Tasten reagieren nicht auf Tippen.

Schritt 2: Start-App prüfen

  1. Sieh dir hier die Gradle-Dateien an:
  • Gradle-Datei auf Projektebene
    In der build.gradle-Datei auf Projektebene sehen Sie die Variablen, die Bibliotheksversionen angeben. Die in der Starter-App verwendeten Versionen funktionieren gut zusammen mit dieser App. Nach Abschluss dieses Codelabs werden Sie möglicherweise von Android Studio aufgefordert, einige der Versionen zu aktualisieren. Sie können selbst entscheiden, ob Sie die in der App enthaltenen Versionen aktualisieren oder beibehalten möchten. Sollte eine Kompilierungsfehler auftreten, versuchen Sie es mit einer Kombination aus Bibliotheksversionen, die in der finalen Lösung-App verwendet werden.
  • Die Gradle-Datei des Moduls. Sie sehen die bereitgestellten Abhängigkeiten für alle Android Jetpack-Bibliotheken, einschließlich Room, und die Abhängigkeiten für Koroutinen.
  1. Sehen Sie sich die Pakete und die Benutzeroberfläche an. Die App ist nach Funktionen strukturiert. Das Paket enthält Platzhalterdateien, die Sie durch das Hinzufügen von Code zu dieser Codelab-Reihe ergänzen.
  • Das database-Paket für den gesamten Code, der sich auf die Datenbank Room bezieht.
  • Die Pakete sleepquality und sleeptracker enthalten für jeden Bildschirm das Fragment, die Modellansicht und die Modellfabrik.
  1. In der Util.kt-Datei finden Sie Funktionen, mit denen Sie die Qualität Ihres Schlafs ermitteln können. Etwas Code ist auskommentiert, weil er auf ein Ansichtsmodell verweist, das Sie später erstellen.
  2. Sehen Sie sich den AndroidTest-Ordner (SleepDatabaseTest.kt) an. Mit diesem Test können Sie prüfen, ob die Datenbank wie vorgesehen funktioniert.

Unter Android werden die Daten in Datenklassen dargestellt. Der Zugriff auf diese Daten erfolgt durch Funktionsaufrufe. In der Datenbankwelt sind jedoch Entitäten und Abfragen erforderlich.

  • Eine Entität ist ein Objekt oder Konzept mit seinen Eigenschaften, das in der Datenbank gespeichert werden soll. Eine Entitätsklasse definiert eine Tabelle und jede Instanz dieser Klasse stellt eine Zeile in der Tabelle dar. Jedes Attribut definiert eine Spalte. Die Entität enthält Informationen zu einem nächtlichen Schlaf.
  • Eine Abfrage ist eine Anfrage für Daten oder Informationen aus einer Datenbanktabelle oder -kombination oder eine Anfrage für eine Aktion für die Daten. Allgemeine Abfragen beziehen sich auf das Abrufen, Einfügen und Aktualisieren von Entitäten. Sie können beispielsweise alle aufgezeichneten Schlafnächte abfragen, sortiert nach Startzeit.

Mit „Room“ erledigen Sie den Großteil der Arbeit von Kotlin-Datenklassen zu Entitäten, die in SQLite-Tabellen gespeichert werden können, von Funktionsdeklarationen bis SQL-Abfragen.

Sie müssen jede Entität als annotierte Datenklasse und die Interaktionen als annotierte Schnittstelle, ein Datenzugriffsobjekt (DAO), definieren. Room nutzt diese annotierten Klassen, um Tabellen in der Datenbank zu erstellen sowie Abfragen zu erstellen, die auf die Datenbank reagieren.

Schritt 1: SleepNight-Entität erstellen

In dieser Aufgabe legen Sie eine Nacht des Schlafs als annotierte Datenklasse fest.

Für eine Nacht solltest du die Startzeit, das Ende und eine Qualitätsbewertung erfassen.

Außerdem brauchst du eine ID, um die Nacht eindeutig zu identifizieren.

  1. Suchen Sie im Paket database die Datei SleepNight.kt und öffnen Sie sie.
  2. Erstelle die SleepNight-Datenklasse mit Parametern für eine ID, eine Startzeit (in Millisekunden), eine Endzeit (in Millisekunden) und eine numerische Bewertung der Schlafqualität.
  • Sie müssen sleepQuality initialisieren. Legen Sie dafür den Wert -1 fest, damit keine Qualitätsdaten erfasst wurden.
  • Außerdem müssen Sie das Ende initialisieren. Stelle es auf die Startzeit, um anzuzeigen, dass noch keine Endzeit aufgezeichnet wurde.
data class SleepNight(
       var nightId: Long = 0L,
       val startTimeMilli: Long = System.currentTimeMillis(),
       var endTimeMilli: Long = startTimeMilli,
       var sleepQuality: Int = -1
)
  1. Vor der Klassendeklaration geben Sie die Datenklasse mit @Entity an. Tabelle benennen: daily_sleep_quality_table Das Argument für tableName ist optional, wird aber empfohlen. Du kannst in der Dokumentation nach anderen Argumenten suchen.

    Wenn du dazu aufgefordert wirst, importiere Entity und alle anderen Annotationen aus der Bibliothek androidx.
@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(...)
  1. Wenn nightId als Primärschlüssel verwendet werden soll, vermerke das Attribut nightId mit @PrimaryKey. Setzen Sie den Parameter autoGenerate auf true, sodass Room die ID für jede Entität generiert. Das garantiert, dass die ID für jede Übernachtung eindeutig ist.
@PrimaryKey(autoGenerate = true)
var nightId: Long = 0L,...
  1. Versieh die übrigen Properties mit @ColumnInfo. Passen Sie die Property-Namen mithilfe von Parametern wie unten beschrieben an.
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(
       @PrimaryKey(autoGenerate = true)
       var nightId: Long = 0L,

       @ColumnInfo(name = "start_time_milli")
       val startTimeMilli: Long = System.currentTimeMillis(),

       @ColumnInfo(name = "end_time_milli")
       var endTimeMilli: Long = startTimeMilli,

       @ColumnInfo(name = "quality_rating")
       var sleepQuality: Int = -1
)
  1. Erstellen Sie Ihren Code und führen Sie ihn aus, um sicherzustellen, dass er keine Fehler enthält.

In dieser Aufgabe definieren Sie ein Datenzugriffsobjekt. Auf Android-Geräten bietet der DAO praktische Methoden zum Einfügen, Löschen und Aktualisieren der Datenbank.

Wenn Sie eine Room-Datenbank verwenden, abfragen Sie die Datenbank ab, indem Sie Kotlin-Funktionen im Code definieren und aufrufen. Diese Kotlin-Funktionen werden SQL-Abfragen zugeordnet. Sie definieren diese Zuordnungen in einem DAO mithilfe von Annotationen. Room erstellt den erforderlichen Code.

Stellen Sie sich einen DAO als eine benutzerdefinierte Schnittstelle für den Zugriff auf Ihre Datenbank vor.

Bei häufigen Datenbankvorgängen enthält die Bibliothek Room Annotationen wie @Insert, @Delete und @Update. Andernfalls gibt es die Annotation @Query. Sie können jede Abfrage schreiben, die von SQLite unterstützt wird.

Wenn Sie Ihre Abfragen in Android Studio erstellen, prüft der Compiler Ihre SQL-Abfragen auf Syntaxfehler.

Für die Schlaf-Tracker-Datenbank des Schlafs müssen Sie Folgendes tun können:

  • Fügen Sie neue Übernachtungen ein.
  • Aktualisieren Sie eine bestehende Nacht, um ein Ende und eine Qualitätsbewertung zu ändern.
  • Rufen Sie eine bestimmte Nacht anhand ihres Schlüssels ab.
  • Alle Nächte erhalten, damit Sie sie sehen können
  • Hol dir die letzte Nacht.
  • Löschen Sie alle Einträge in der Datenbank.

Schritt 1: SleepDatabase-DAO erstellen

  1. Öffnen Sie im Paket database das Feld SleepDatabaseDao.kt.
  2. Beachten Sie, dass interface SleepDatabaseDao mit @Dao annotiert ist. Alle DAOs müssen mit dem Keyword @Dao versehen werden.
@Dao
interface SleepDatabaseDao {}
  1. Füge im Text der Schnittstelle eine @Insert-Annotation hinzu. Fügen Sie unter @Insert eine insert()-Funktion hinzu, die eine Instanz der Entity-Klasse SleepNight als Argument verwendet.

    Richtig. Room generiert den erforderlichen Code, um SleepNight in die Datenbank einzufügen. Wenn Sie insert() aus Ihrem Kotlin-Code aufrufen, führt Room eine SQL-Abfrage aus, um die Entität in die Datenbank einzufügen. (Hinweis: Sie können die Funktion beliebig aufrufen.)
@Insert
fun insert(night: SleepNight)
  1. Füge eine @Update-Annotation mit einer update()-Funktion für eine SleepNight hinzu. Die Entität, die aktualisiert wurde, ist die Entität, die denselben Schlüssel wie die übergebene Entität hat. Du kannst einige oder alle anderen Eigenschaften der Entität aktualisieren.
@Update
fun update(night: SleepNight)

Es gibt keine Komfort-Anmerkung für die verbleibende Funktionalität, daher müssen Sie die Annotation @Query verwenden und SQLite-Abfragen bereitstellen.

  1. Füge eine @Query-Anmerkung mit einer get()-Funktion hinzu, die das Argument Long key übernimmt und eine NULL-gültige SleepNight zurückgibt. Ihnen wird eine Fehlermeldung aufgrund eines fehlenden Parameters angezeigt.
@Query
fun get(key: Long): SleepNight?
  1. Die Abfrage wird als Stringparameter in der Anmerkung bereitgestellt. Fügen Sie @Query einen Parameter hinzu. Machen Sie es zu einer String-Abfrage.
  • Alle Spalten der daily_sleep_quality_table auswählen
  • WHERE entspricht nightId dem Argument "key".

    Das ist der :key. Sie verwenden die Doppelpunktschreibung in der Abfrage, um auf Argumente in der Funktion zu verweisen.
("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
  1. Fügen Sie @Query noch einmal mit einer clear()-Funktion und einer SQLite-Abfrage zu DELETE von daily_sleep_quality_table hinzu. Durch diese Abfrage wird die Tabelle selbst nicht gelöscht.

    Mit der Annotation @Delete wird ein Element gelöscht. Du kannst beispielsweise @Delete verwenden und eine Liste der Übernachtungen angeben, die gelöscht werden sollen. Der Nachteil besteht darin, dass Sie die Tabelleninformationen abrufen oder kennen müssen. Die Annotation @Delete eignet sich hervorragend zum Löschen bestimmter Einträge. Für das Löschen aller Einträge aus einer Tabelle ist sie jedoch nicht effizient.
@Query("DELETE FROM daily_sleep_quality_table")
fun clear()
  1. Fügen Sie einen @Query mit einer getTonight()-Funktion hinzu. Setzen Sie die von getTonight() zurückgegebene SleepNight auf NULL, damit die Funktion die Groß- und Kleinschreibung verarbeiten kann, wenn die Tabelle leer ist. Die Tabelle ist anfangs leer und nach dem Löschen der Daten.

    Wenn Sie aus der Datenbank eine Abfrage abrufen möchten, schreiben Sie eine SQLite-Abfrage, mit der das erste Element einer Liste von Ergebnissen nach nightId in absteigender Reihenfolge zurückgegeben wird. Verwende LIMIT 1, um nur ein Element zurückzugeben.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
fun getTonight(): SleepNight?
  1. Füge ein @Query mit einer getAllNights()-Funktion hinzu:
  • Lassen Sie die SQLite-Abfrage alle Spalten aus der daily_sleep_quality_table zurückgeben, sortiert in absteigender Reihenfolge.
  • getAllNights() muss eine Liste von SleepNight-Entitäten als LiveData zurückgeben. Room hält diesen LiveData für Sie auf dem neuesten Stand. Sie müssen also die Daten nur einmal abrufen.
  • Möglicherweise müssen Sie LiveData aus androidx.lifecycle.LiveData importieren.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
fun getAllNights(): LiveData<List<SleepNight>>
  1. Die sichtbaren Änderungen werden zwar nicht angezeigt, Führe deine App aber noch einmal aus, um sicherzustellen, dass sie keine Fehler enthält.

In dieser Aufgabe erstellen Sie eine Room-Datenbank, die die Entity und den DAO verwendet, die Sie in der vorherigen Aufgabe erstellt haben.

Sie müssen eine abstrakte Inhaber-Klasse für die Datenbank erstellen, die mit @Database gekennzeichnet ist. Diese Klasse hat eine Methode, die entweder eine Instanz der Datenbank erstellt, wenn die Datenbank nicht existiert, oder einen Verweis auf eine vorhandene Datenbank zurückgibt.

Das Abrufen einer Room-Datenbank ist etwas kompliziert. Hier ist der allgemeine Ablauf hier, bevor Sie mit dem Code beginnen:

  • Erstelle eine public abstract-Klasse, die extends RoomDatabase. Diese Klasse dient als Datenbankinhaber. Die Klasse ist abstrakt, da Room die Implementierung für Sie erstellt.
  • Annotieren Sie den Kurs mit @Database. Geben Sie in den Argumenten die Entitäten für die Datenbank an und legen Sie die Versionsnummer fest.
  • Definiere in einem companion-Objekt eine abstrakte Methode oder Eigenschaft, die ein SleepDatabaseDao zurückgibt. Der Text wird von Room generiert.
  • Da Sie nur eine Instanz der Room-Datenbank für die gesamte Anwendung benötigen, machen Sie die RoomDatabase zu einem Singleton.
  • Mit dem Datenbank-Builder von Room können Sie die Datenbank nur erstellen, wenn sie nicht vorhanden ist. Andernfalls wird die vorhandene Datenbank zurückgegeben.

Schritt 1: Datenbank erstellen

  1. Öffnen Sie im Paket database das Feld SleepDatabase.kt.
  2. Erstellen Sie in der Datei eine abstract-Klasse namens SleepDatabase, die RoomDatabase erweitert.

    Annotieren Sie den Kurs mit @Database.
@Database()
abstract class SleepDatabase : RoomDatabase() {}
  1. Ihnen wird eine Fehlermeldung für fehlende Entitäten und Versionsparameter angezeigt. Die Annotation @Database erfordert mehrere Argumente, damit Room die Datenbank erstellen kann.
  • Geben Sie SleepNight als einzigen Artikel mit der Liste entities an.
  • Legen Sie version als 1 fest. Jedes Mal, wenn Sie das Schema ändern, müssen Sie die Versionsnummer erhöhen.
  • Setzen Sie exportSchema auf false, damit keine Sicherungen des Schemaversionsverlaufs gespeichert werden.
entities = [SleepNight::class], version = 1, exportSchema = false
  1. Die Datenbank muss den DAO kennen. Geben Sie im Textkörper der Klasse einen abstrakten Wert an, der SleepDatabaseDao zurückgibt. Sie können mehrere DAOs haben.
abstract val sleepDatabaseDao: SleepDatabaseDao
  1. Definieren Sie darunter ein companion-Objekt. Das Companion-Objekt ermöglicht Clients den Zugriff auf die Methoden zum Erstellen oder Abrufen der Datenbank, ohne die Klasse zu instanziieren. Da der einzige Zweck dieser Klasse darin besteht, eine Datenbank bereitzustellen, ist es nie sinnvoll, sie zu instanziieren.
 companion object {}
  1. Erklären Sie im companion-Objekt eine private Null-Variable INSTANCE für die Datenbank und initialisieren Sie sie in null. Die Variable INSTANCE behält einen Verweis auf die Datenbank bei, sobald eine erstellt wurde. So vermeiden Sie, dass Sie immer wieder Verbindungen zur Datenbank öffnen müssen, was teuer ist.

Annotieren Sie INSTANCE mit @Volatile. Der Wert einer flüchtigen Variablen wird nie im Cache gespeichert und alle Schreibvorgänge und Lesevorgänge erfolgen im und vom Hauptarbeitsspeicher. So ist der Wert von INSTANCE immer auf dem neuesten Stand und für alle Ausführungsthreads gleich. Das bedeutet, dass Änderungen, die an einem Thread an INSTANCE vorgenommen werden, sofort für alle anderen Threads sichtbar sind. Sie erhalten z. B. keine Situation, in der zwei Threads dieselbe Entität in einem Cache aktualisieren. Das würde zu einem Problem führen.

@Volatile
private var INSTANCE: SleepDatabase? = null
  1. Definieren Sie unter INSTANCE noch im companion-Objekt eine getInstance()-Methode mit einem Context-Parameter, den der Datenbank-Builder benötigt. Geben Sie einen Typ SleepDatabase zurück. Sie sehen eine Fehlermeldung, da getInstance() noch nichts zurückgibt.
fun getInstance(context: Context): SleepDatabase {}
  1. Fügen Sie in getInstance() einen synchronized{}-Block ein. Übergeben Sie this, damit Sie auf den Kontext zugreifen können.

    Mehrere Threads können gleichzeitig nach einer Datenbankinstanz fragen. Dies führt zu zwei statt einer Datenbank. In dieser Beispiel-App tritt dieses Problem wahrscheinlich nicht auf. Bei einer komplexeren App ist das aber möglich. Wenn Sie den Code umschließen, um die Datenbank in synchronized zu speichern, kann der Codeblock nur jeweils für einen Ausführungsthread eingegeben werden. Dadurch wird die Datenbank nur einmal initialisiert.
synchronized(this) {}
  1. Kopieren Sie im synchronisierten Block den aktuellen Wert von INSTANCE in die lokale Variable instance. Hierdurch wird Smart Cast genutzt, das nur für lokale Variablen verfügbar ist.
var instance = INSTANCE
  1. Im synchronized-Block befindet sich return instance am Ende des synchronized-Blocks. Ignorieren Sie den Fehler wegen nicht übereinstimmender Rückgabetypen. Nach der Fertigstellung werden keine Werte zurückgegeben.
return instance
  1. Fügen Sie oberhalb der Anweisung return eine if-Anweisung hinzu, um zu prüfen, ob instance null ist, wenn also noch keine Datenbank vorhanden ist.
if (instance == null) {}
  1. Wenn instance den Wert null hat, verwenden Sie den Database Builder, um eine Datenbank abzurufen. Rufen Sie im Text der if-Anweisung Room.databaseBuilder auf und geben Sie den übergebenen Kontext, die Datenbankklasse und den Namen der Datenbank sleep_history_database an. Um den Fehler zu entfernen, müssen Sie in den folgenden Schritten eine Migrationsstrategie und build() hinzufügen.
instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database")
  1. Fügen Sie dem Builder die erforderliche Migrationsstrategie hinzu. Verwende .fallbackToDestructiveMigration().

    In der Regel musst du ein Migrationsobjekt mit einer Migrationsstrategie für den Zeitpunkt angeben, an dem sich das Schema ändert. Ein Migrationsobjekt ist ein Objekt, das definiert, wie alle Zeilen mit dem alten Schema aufgenommen werden, und sie in Zeilen im neuen Schema umwandeln, damit keine Daten verloren gehen. Migration wird in diesem Codelab nicht genutzt. Eine einfache Lösung ist das Löschen und Neubau der Datenbank, wodurch die Daten verloren gehen.
.fallbackToDestructiveMigration()
  1. Rufen Sie schließlich .build() auf.
.build()
  1. Weisen Sie INSTANCE = instance als letzten Schritt in der if-Anweisung zu.
INSTANCE = instance
  1. Der endgültige Code sollte so aussehen:
@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract class SleepDatabase : RoomDatabase() {

   abstract val sleepDatabaseDao: SleepDatabaseDao

   companion object {

       @Volatile
       private var INSTANCE: SleepDatabase? = null

       fun getInstance(context: Context): SleepDatabase {
           synchronized(this) {
               var instance = INSTANCE

               if (instance == null) {
                   instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database"
                   )
                           .fallbackToDestructiveMigration()
                           .build()
                   INSTANCE = instance
               }
               return instance
           }
       }
   }
}
  1. Erstellen Sie Ihren Code und führen Sie ihn aus.

Sie haben jetzt alle Grundbausteine für Ihre Room-Datenbank. Dieser Code wird kompiliert und ausgeführt, aber Sie können nicht feststellen, ob er tatsächlich funktioniert. Jetzt ist es an der Zeit, einige grundlegende Tests hinzuzufügen.

Schritt 2: SleepDatabase testen

In diesem Schritt testen Sie, ob die Datenbank funktioniert. Dadurch wird sichergestellt, dass die Datenbank funktioniert, bevor Sie darauf aufbauen. Die bereitgestellten Tests sind einfach. Bei einer Produktions-App würden Sie alle Funktionen und Abfragen in allen DAOs ausführen.

Die Starter-App enthält einen androidTest-Ordner. Dieser androidTest-Ordner enthält Einheitentests, die Android-Instrumentierung beinhalten. Das ist fälschlicherweise so, dass die Tests das Android-Framework erfordern. Sie müssen die Tests also auf einem physischen oder virtuellen Gerät durchführen. Selbstverständlich können Sie auch reine Unittests erstellen und durchführen, die das Android-Framework nicht beinhalten.

  1. Öffnen Sie in Android Studio im Ordner androidTest die Datei SleepDatabaseTest.
  2. Wenn du die Kommentierung des Codes aufheben möchtest, wähle den gesamten kommentierten Code aus und drücke die Tastenkombination Cmd+/ oder Control+/.
  3. Sehen Sie sich die Datei an.

Hier ein kurzer Überblick über den Testcode, den du wiederverwenden kannst:

  • SleepDabaseTest ist eine Testklasse.
  • Die Annotation @RunWith gibt den Test-Ausführer an, also das Programm, das die Tests einrichtet und ausführt.
  • Während der Einrichtung wird die mit @Before annotierte Funktion ausgeführt und erstellt eine speicherinterne SleepDatabase mit der SleepDatabaseDao. „In-Memory“ bedeutet, dass diese Datenbank nicht im Dateisystem gespeichert und nach Ablauf der Tests gelöscht wird.
  • Außerdem ruft der Code beim Erstellen der In-Memory-Datenbank eine andere testspezifische Methode auf: allowMainThreadQueries. Wenn Sie versuchen, Abfragen im Hauptthread auszuführen, wird standardmäßig eine Fehlermeldung angezeigt. Mit dieser Methode können Sie Tests im Hauptthread ausführen. Dies sollten Sie nur während der Tests tun.
  • In einer mit @Test annotierten Testmethode erstellen, einfügen und abrufen Sie eine SleepNight und behaupten, dass sie identisch sind. Wenn etwas nicht funktioniert, lösen Sie eine Ausnahme aus. In einem echten Test gibt es mehrere @Test Methoden.
  • Wenn der Test abgeschlossen ist, wird die mit @After gekennzeichnete Funktion ausgeführt, um die Datenbank zu schließen.
  1. Klicken Sie im Bereich Projekt mit der rechten Maustaste auf die Testdatei und wählen Sie Ausführen 'SleepDatabaseTest' aus.
  2. Prüfen Sie nach dem Ausführen der Tests im Bereich SleepDatabaseTest, ob alle Tests bestanden wurden.

Da alle Tests bestanden wurden, wissen Sie jetzt mehrere Dinge:

  • Die Datenbank wird korrekt erstellt.
  • Sie können ein SleepNight in die Datenbank einfügen.
  • Du kannst SleepNight wiederherstellen.
  • SleepNight hat den richtigen Wert für die Qualität.

Android Studio-Projekt: TrackMySleepqualityRoomAndTesting

Beim Testen einer Datenbank müssen Sie alle in der DAO definierten Methoden anwenden. Um die Tests abzuschließen, können Sie Tests durchführen und die anderen DAO-Methoden ausführen.

  • Definieren Sie Ihre Tabellen als Datenklassen, die mit @Entity gekennzeichnet sind. Definieren Sie mit @ColumnInfo verknüpfte Spalten als Spalten in den Tabellen.
  • Legen Sie ein Datenzugriffsobjekt (DAO) als Schnittstelle mit Annotationen mit @Dao fest. Der DAO ordnet Kotlin-Funktionen Datenbankabfragen zu.
  • Mit Annotationen können Sie die Funktionen @Insert, @Delete und @Update definieren.
  • Verwenden Sie die Annotation @Query mit einem SQLite-Abfragestring als Parameter für alle anderen Abfragen.
  • Erstelle eine abstrakte Klasse mit einer getInstance()-Funktion, die eine Datenbank zurückgibt.
  • Mit instrumentierten Tests können Sie testen, ob Ihre Datenbank und der DAO wie erwartet funktionieren. Sie können die bereitgestellten Tests als Vorlage verwenden.

Udacity-Kurs:

Android-Entwicklerdokumentation:

Weitere Dokumentationen und Artikel:

In diesem Abschnitt werden mögliche Hausaufgaben für Schüler oder Studenten aufgeführt, die an diesem von einem Kursleiter geleiteten Codelab arbeiten. Die Lehrkraft kann Folgendes tun:

  • Bei Bedarf können Sie die entsprechenden Aufgaben zuweisen.
  • Schülern mitteilen, wie sie Aufgaben für die Aufgabe abgeben
  • Benoten Sie die Hausaufgaben.

Lehrkräfte können diese Vorschläge so oft oder so oft verwenden, wie sie möchten. anderen Aufgaben können sie nach Belieben zugewiesen werden.

Wenn Sie alleine an diesem Codelab arbeiten, können Sie Ihr Wissen mit diesen Hausaufgaben testen.

Diese Fragen beantworten

Frage 1

Wie geben Sie an, dass eine Klasse eine Entität darstellt, die in einer Room-Datenbank gespeichert werden soll?

  • Machen Sie den Kurs um DatabaseEntity.
  • Annotieren Sie den Kurs mit @Entity.
  • Annotieren Sie den Kurs mit @Database.
  • Erweitern Sie den Kurs um RoomEntity und versehen Sie ihn mit @Room.

Frage 2

Der DAO (data access object) ist eine Schnittstelle, die Room verwendet, um Kotlin-Funktionen Datenbankabfragen zuzuordnen.

Wie geben Sie an, dass eine Schnittstelle einen DAO für eine Room-Datenbank darstellt?

  • Erweitern Sie die Schnittstelle um RoomDAO.
  • Dafür muss die Schnittstelle EntityDao erweitern und dann die Methode DaoConnection() implementieren.
  • Annotieren Sie die Schnittstelle mit @Dao.
  • Annotieren Sie die Schnittstelle mit @RoomConnection.

Frage 3

Welche der folgenden Aussagen über die Room-Datenbank trifft zu? Wählen Sie alle zutreffenden Antworten aus.

  • Sie können Tabellen für eine Room-Datenbank als annotierte Datenklassen definieren.
  • Wenn du LiveDatavon einer Abfrage aus zurückgibst, wird Room für dich LiveDataaktualisiert, wenn sich der LiveData ändert.
  • Jede Room-Datenbank muss genau eine DAO haben.
  • Wenn Sie eine Klasse als Room-Datenbank kennzeichnen möchten, machen Sie sie zu einer Unterklasse von RoomDatabase und annotieren Sie sie mit @Database.

Frage 4

Welche der folgenden Annotationen können Sie in Ihrer @Dao-Schnittstelle verwenden? Wählen Sie alle zutreffenden Antworten aus.

  • @Get
  • @Update
  • @Insert
  • @Query

Frage 5

Wie können Sie überprüfen, ob Ihre Datenbank funktioniert? Wählen Sie alle zutreffenden Antworten aus.

  • Instrumentierte Tests schreiben.
  • Schreiben Sie die App und führen Sie sie aus, bis die Daten angezeigt werden.
  • Ersetzen Sie die Aufrufe der Methoden in der DAO-Schnittstelle durch Aufrufe gleichwertiger Methoden in der Klasse Entity.
  • Führe die Funktion verifyDatabase() aus, die von der Bibliothek Room bereitgestellt wird.

Beginnen Sie mit der nächsten Lektion: 6.2 Koroutinen und Raum

Links zu anderen Codelabs in diesem Kurs finden Sie auf der Landingpage zu Kotlin-Grundlagen für Android.