Damit Abfragen die Benutzeroberfläche nicht blockieren, unterstützt Room den Datenbankzugriff im Hauptthread nicht. Daher müssen Sie Ihre DAO-Abfragen asynchron ausführen. Die Room-Bibliothek bietet Integrationen mit mehreren Frameworks, um die asynchrone Abfrageausführung zu ermöglichen.
DAO-Abfragen lassen sich in drei Kategorien unterteilen:
- One-Shot-Schreibabfragen, mit denen Daten in die Datenbank eingefügt, aktualisiert oder gelöscht werden.
- One-Shot-Leseabfragen , mit denen Daten nur einmal aus der Datenbank gelesen werden und ein Ergebnis mit dem Snapshot der Datenbank zu diesem Zeitpunkt zurückgegeben wird.
- Beobachtbare Leseabfragen , mit denen Daten jedes Mal aus der Datenbank gelesen werden, wenn sich die zugrunde liegenden Datenbanktabellen ändern. Außerdem werden neue Werte ausgegeben, um diese Änderungen widerzuspiegeln.
Sprach- und Framework-Optionen
Room bietet Integrationsunterstützung für die Interoperabilität mit bestimmten Sprachfunktionen und Bibliotheken. In der folgenden Tabelle sind die anwendbaren Rückgabetypen basierend auf dem Abfragetyp und dem Framework aufgeführt:
| Abfragetyp | Kotlin-Sprachfunktionen (nativ) | RxJava | Guava | Jetpack Lifecycle* |
|---|---|---|---|---|
| One-Shot-Schreibabfrage | Koroutinen (suspend) |
Single<T>, Maybe<T>,
Completable |
ListenableFuture<T> |
– |
| One-Shot-Leseabfrage | Koroutinen (suspend) |
Single<T>, Maybe<T> |
ListenableFuture<T> |
– |
| Beobachtbare Leseabfrage | Flow<T> |
Flowable<T>, Publisher<T>,
Observable<T> |
– | LiveData<T> |
In diesem Leitfaden werden drei Möglichkeiten gezeigt, wie Sie diese Integrationen verwenden können, um asynchrone Abfragen in Ihren DAOs zu implementieren.
Kotlin mit Flow und Koroutinen
Kotlin bietet integrierte Sprachfunktionen, mit denen Sie asynchrone Abfragen ohne Frameworks von Drittanbietern schreiben können:
- Room unterstützt direkt den Flow von Kotlin, um beobachtbare Abfragen zu schreiben.
- Room erfordert das
suspendSchlüsselwort, um Ihre One-Shot-DAO-Abfragen asynchron mit Kotlin-Koroutinen auszuführen.
Die Unterstützung für Koroutinen und Flow ist direkt in die Room-Laufzeitumgebung integriert, sodass keine zusätzlichen Artefakte erforderlich sind.
RxJava für Kotlin und Java
Room 3.0 unterstützt RxJava 3-Rückgabetypen. Wenn Sie RxJava-Rückgabetypen verwenden möchten, müssen Sie die RxJava-Rückgabetypkonverter in Ihrer Datenbank oder Ihrem DAO registrieren:
- Fügen Sie das Artefakt
androidx.room3:room3-rxjava3in Ihre Build-Konfiguration ein. - Markieren Sie Ihre
@Databaseoder@DaoDeklaration mit@DaoReturnTypeConverters(RxDaoReturnTypeConverters::class).
Room unterstützt die folgenden RxJava 3-Rückgabetypen:
- One-Shot-Abfragen:
Completable,Single<T>, undMaybe<T> - Beobachtbare Abfragen:
Publisher<T>,Flowable<T>, undObservable<T>
LiveData und Guava
Room 3.0 unterstützt LiveData- und Guava-ListenableFuture-Rückgabetypen mithilfe von Konvertern:
- LiveData: Fügen Sie das Artefakt
androidx.room3:room3-livedataein und markieren Sie Ihre Datenbank oder Ihr DAO mit@DaoReturnTypeConverters(LiveDataDaoReturnTypeConverter::class). - Guava: Fügen Sie das Artefakt
androidx.room3:room3-guavaein und markieren Sie Ihre Datenbank oder Ihr DAO mit@DaoReturnTypeConverters(GuavaDaoReturnTypeConverter::class).
Asynchrone One-Shot-Abfragen schreiben
One-Shot-Abfragen sind Datenbankvorgänge, die nur einmal ausgeführt werden und zum Zeitpunkt der Ausführung einen Snapshot der Daten erstellen. Hier sind einige Beispiele für asynchrone One-Shot-Abfragen:
@Dao interface UserDao { @Query("SELECT * FROM user WHERE id = :id") suspend fun loadUserById(id: Int): User @Query("SELECT * from user WHERE region IN (:regions)") suspend fun loadUsersByRegion(regions: List<String>): List<User> }
Beobachtbare Abfragen schreiben
Beobachtbare Abfragen sind Lesevorgänge, die neue Werte ausgeben, wenn sich die referenzierten Tabellen ändern. Sie können dieses Verhalten beispielsweise verwenden, um eine angezeigte Liste von Elementen zu aktualisieren, wenn sich die Datenbank ändert. Hier sind einige Beispiele für beobachtbare Abfragen:
@Dao interface ObservableUserDao { @Query("SELECT * FROM user WHERE id = :id") fun loadUserById(id: Int): Flow<User> @Query("SELECT * from user WHERE region IN (:regions)") fun loadUsersByRegion(regions: List<String>): Flow<List<User>> }
Datenbankinvalidierung manuell verfolgen
Wenn Sie beobachtbare Datenbankvorgänge manuell erstellen müssen, können Sie die
createFlow API von InvalidationTracker verwenden. Mit dieser API können Sie einen Flow erstellen, der Änderungen an bestimmten Tabellen verfolgt und eine Benachrichtigung ausgibt, wenn sich diese Tabellen ändern.
fun getArtistTours(db: RoomDatabase, from: Date, to: Date): Flow<Map<Artist, TourState>> { return db.invalidationTracker.createFlow("Artist").map { _ -> val artists = artistsDao.getAllArtists() val tours = tourService.fetchStates(artists.map { it.id }) associateTours(artists, tours, from, to) } }
Standardmäßig gibt der zurückgegebene Flow einen Anfangswert mit allen registrierten Tabellen aus, um den Stream zu starten. Sie können dieses Verhalten deaktivieren, indem Sie den Parameter emitInitialState auf false setzen.
Benutzerdefinierte DAO-Rückgabetypkonverter
Für Typen, die von Room oder den zugehörigen Erweiterungsbibliotheken nicht direkt unterstützt werden, können Sie benutzerdefinierte DAO-Rückgabetypkonverter definieren, um zusätzliche Rückgabetypen zu unterstützen. Wenn Sie das Ergebnis einer DAO-Funktion in Ihren benutzerdefinierten Typ umwandeln möchten,
markieren Sie eine Konverterfunktion mit @DaoReturnTypeConverter.
Sie können beispielsweise einen Konverter definieren, der androidx.tracing verwendet, um Trace-Abschnitte um die Ausführung einer Abfrage hinzuzufügen. So können Sie leistungsempfindliche Abfragen überwachen, indem Sie die Ausführung in einen benutzerdefinierten TracedQuery-Typ einbinden:
class TracedQuery<T>(val result: T) object TracingDaoReturnTypeConverter { @DaoReturnTypeConverter([OperationType.READ]) suspend fun <T> convert( rawQuery: RoomRawQuery, executeAndConvert: suspend () -> T ): TracedQuery<T> { val result = trace("TracedQuery: ${rawQuery.sql}") { executeAndConvert() } return TracedQuery(result) } }
Wenn Sie den Konverter verwenden möchten, markieren Sie Ihre Datenbank oder Ihr DAO mit
@DaoReturnTypeConverters:
@Dao @DaoReturnTypeConverters(TracingDaoReturnTypeConverter::class) interface MusicDao { @Query("SELECT * FROM Song") suspend fun getAllSongs(): TracedQuery<List<Song>> }
Initialisierung des DAO-Rückgabetypkonverters steuern
Normalerweise übernimmt Room die Instanziierung von DAO-Rückgabetypkonvertern.
Wenn Sie jedoch zusätzliche Abhängigkeiten an Ihre Konverterklassen übergeben müssen, muss Ihre App die Initialisierung direkt steuern. Markieren Sie in diesem Fall Ihre
Konverterklasse mit @ProvidedDaoReturnTypeConverter:
@ProvidedDaoReturnTypeConverter class TracingDaoReturnTypeConverter(val tracer: Tracer) { @DaoReturnTypeConverter([OperationType.READ]) suspend fun <T> convert( rawQuery: RoomRawQuery, executeAndConvert: suspend () -> T ): TracedQuery<T> { val result = tracer.trace("TracedQuery: ${rawQuery.sql}") { executeAndConvert() } return TracedQuery(result) } }
Zusätzlich zur Deklaration Ihrer Konverterklasse in
@DaoReturnTypeConverters, verwenden Sie die
RoomDatabase.Builder.addDaoReturnTypeConverter Funktion, um eine
Instanz Ihrer Konverterklasse an den RoomDatabase Builder zu übergeben:
val db = Room.databaseBuilder<MyDatabase>(applicationContext, "database-name") .addDaoReturnTypeConverter(TracingDaoReturnTypeConverter(myLoggerInstance)) .build()
Anforderungen an Konverterfunktionen
Eine @DaoReturnTypeConverter-Funktion muss mehrere Anforderungen erfüllen:
- Sie muss einen funktionalen Parameter als letztes Argument haben, der normalerweise
executeAndConvertheißt. Dieser Parameter ist einsuspend-Lambda, das von Room generiert wird, um die Abfrage auszuführen und das Ergebnis zu parsen.- Wenn der Konverter die Abfrage transformieren muss, z. B. Paging, kann das Lambda einen
RoomRawQuery-Parameter verwenden.
- Wenn der Konverter die Abfrage transformieren muss, z. B. Paging, kann das Lambda einen
- Optional können vor dem Lambda die folgenden Parameter akzeptiert werden:
db: RoomDatabase: Greift auf die Datenbankinstanz zu, was nützlich ist, um den Koroutinenbereich abzurufen oder zusätzliche Vorgänge auszuführen.tableNames: Array<String>oderList<String>: Gibt die Namen der Tabellen an, auf die von der Abfrage zugegriffen wird. Dies ist für beobachtbare Typen nützlich.rawQuery: RoomRawQuery: Gibt die Laufzeitinstanz der Abfrage an.inTransaction: Boolean: Gibt an, ob die Abfrage innerhalb einer Transaktion ausgeführt wird.