為防止查詢封鎖 UI,Room 不支援在主執行緒上存取資料庫。這項限制表示您必須將 DAO 查詢設為非同步。Room 程式庫包含多種架構的整合,以提供非同步查詢執行作業。
DAO 查詢分為三個類別:
- 「單一寫入」查詢,可在資料庫中插入、更新或刪除資料。
- 「單一讀取」查詢,只會從資料庫讀取資料一次,並傳回當時資料庫的快照結果。
- 「可觀測讀取」查詢,可在基礎資料庫資料表變更時從資料庫讀取資料,並傳回新的值以反映這些變更。
語言和架構選項
Room 提供整合支援,以實現與特定語言功能和資料庫的互通性。下表顯示了以查詢類型和架構為基礎的適用傳回類型:
| 查詢類型 | Kotlin 語言功能 (原生) | RxJava | Guava | Jetpack Lifecycle* |
|---|---|---|---|---|
| 單一寫入 | 協同程式 (suspend) |
Single<T>、Maybe<T>、Completable |
ListenableFuture<T> |
不適用 |
| 單一讀取 | 協同程式 (suspend) |
Single<T>、Maybe<T> |
ListenableFuture<T> |
不適用 |
| 可觀測讀取 | Flow<T> |
Flowable<T>、Publisher<T>、Observable<T> |
不適用 | LiveData<T> |
本指南說明利用整合功能在 DAO 中實作非同步查詢的三種方法。
Kotlin 搭配 Flow 和協同程式
Kotlin 提供內建語言功能,可讓您在不使用第三方架構的情況下,寫入非同步查詢:
- Room 直接支援 Kotlin 的 Flow,可撰寫可觀測的查詢。
- Room 需要
suspend關鍵字,才能使用 Kotlin 協同程式進行非同步 DAO 單次查詢。
協同程式和 Flow 支援功能直接內建於核心 Room 執行階段,因此不需要額外構件。
適用於 Kotlin 和 Java 的 RxJava
Room 3.0 支援 RxJava 3 傳回類型。如要使用 RxJava 傳回類型,您必須在資料庫或 DAO 中註冊 RxJava 傳回類型轉換器:
- 在建構設定中加入
androidx.room3:room3-rxjava3構件。 - 使用
@DaoReturnTypeConverters(RxDaoReturnTypeConverters::class)為@Database或@Dao宣告加上註解。
Room 支援下列 RxJava 3 傳回類型:
- 單樣本查詢:
Completable、Single<T>,以及Maybe<T> - 可觀察的查詢:
Publisher<T>、Flowable<T>, 以及Observable<T>
LiveData 和 Guava
Room 3.0 支援使用轉換器,傳回 LiveData 和 Guava ListenableFuture 類型:
- LiveData:加入
androidx.room3:room3-livedata構件,並使用@DaoReturnTypeConverters(LiveDataDaoReturnTypeConverter::class)註解資料庫或 DAO。 - Guava:加入
androidx.room3:room3-guava構件,並使用@DaoReturnTypeConverters(GuavaDaoReturnTypeConverter::class)註解資料庫或 DAO。
撰寫非同步單一查詢
單一查詢是指僅執行一次、擷取執行當下資料快照的資料庫作業。以下列舉幾種非同步單一查詢:
@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> }
撰寫可觀測查詢
可觀測查詢屬於讀取作業,在參照的資料表發生變更時,即會觸發新的值。舉例來說,您可以使用這項行為,在資料庫變更時,讓顯示的項目清單保持在最新狀態。以下列舉幾個可觀測的查詢:
@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>> }
手動追蹤資料庫失效
需要手動建構可觀測的資料庫作業時,可以使用 InvalidationTracker 的 createFlow API。這個 API 可讓您建立 Flow,追蹤特定資料表的修改內容,並在這些資料表變更時發出通知。
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) } }
根據預設,傳回的 Flow 會發出包含所有已註冊資料表的初始值,以啟動串流。如要停用這項行為,請將 emitInitialState 參數設為 false。
自訂 DAO 傳回類型轉換器
對於 Room 或其擴充功能程式庫未直接支援的型別,您可以定義自訂 DAO 傳回類型轉換器,以支援其他傳回類型。如要將 DAO 函式的結果轉換為自訂型別,請使用 @DaoReturnTypeConverter 註解標註轉換器函式。
舉例來說,您可以定義使用 androidx.tracing 的轉換器,在查詢執行作業周圍新增追蹤區段,方法是將執行作業包裝在自訂 TracedQuery 型別中,藉此監控對效能要求嚴格的查詢:
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) } }
如要使用轉換器,請將 @DaoReturnTypeConverters 註解加到資料庫或 DAO:
@Dao @DaoReturnTypeConverters(TracingDaoReturnTypeConverter::class) interface MusicDao { @Query("SELECT * FROM Song") suspend fun getAllSongs(): TracedQuery<List<Song>> }
控制 DAO 傳回類型轉換器初始化
一般而言,Room 會處理 DAO 傳回類型轉換器的例項建立作業。不過,如果您必須將其他依附元件傳遞至轉換器類別,應用程式就必須直接控管這些依附元件的初始化作業。如果是,請將 @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) } }
接著,除了在 @DaoReturnTypeConverters 中宣告轉換器類別之外,請使用 RoomDatabase.Builder.addDaoReturnTypeConverter 函式,將轉換器類別的例項傳遞至 RoomDatabase 建構工具:
val db = Room.databaseBuilder<MyDatabase>(applicationContext, "database-name") .addDaoReturnTypeConverter(TracingDaoReturnTypeConverter(myLoggerInstance)) .build()
轉換函式規定
@DaoReturnTypeConverter 函式必須符合下列幾項規定:
- 最後一個引數必須是函式參數,通常命名為
executeAndConvert。這個參數是 Room 產生的suspendlambda,用於執行查詢及剖析結果。- 如果轉換器需要轉換查詢 (例如分頁),lambda 可以採用
RoomRawQuery參數。
- 如果轉換器需要轉換查詢 (例如分頁),lambda 可以採用
- 這個函式可選擇性地接受 Lambda 前的下列參數:
db: RoomDatabase:存取資料庫執行個體,有助於取得協同程式範圍或執行其他作業。tableNames: Array<String>或List<String>:提供查詢存取的表格名稱,適用於可觀察的型別。rawQuery: RoomRawQuery:提供查詢的執行階段例項。inTransaction: Boolean:指出查詢是否在交易中執行。