为防止查询阻止界面,Room 不支持在主线程上访问数据库。此限制意味着您必须将 DAO 查询 设为异步。Room 库包含与多个框架的集成,以提供异步查询执行功能。
DAO 查询分为三类:
- 单次写入查询,用于在数据库中插入数据或者更新或删除其中的数据。
- 单次读取查询,是指仅从数据库中读取一次数据,并在执行时返回带有数据库快照的结果。
- 可观察读取查询,是指每当底层数据库表发生变化时,都会从数据库中读取数据,并发出新值来反映这些更改。
语言和框架选项
Room 可为涉及特定语言功能和库的互操作性提供集成支持。下表根据查询类型和框架展示了适用的返回值类型:
| 查询类型 | Kotlin 语言功能(原生) | RxJava | 番石榴粉 | Jetpack 生命周期* |
|---|---|---|---|---|
| 单次写入 | 协程 (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 返回值转换器:
- 在 build 配置中添加
androidx.room3:room3-rxjava3工件。 - 使用
@DaoReturnTypeConverters(RxDaoReturnTypeConverters::class)为您的@Database或@Dao声明添加注解。
Room 支持以下 RxJava 3 返回值类型:
- 单次查询:
Completable、Single<T>和Maybe<T> - 可观察查询:
Publisher<T>、Flowable<T>和Observable<T>
LiveData 和番石榴粉
Room 3.0 使用转换器支持 LiveData 和番石榴粉 ListenableFuture 返回值类型:
- LiveData:添加
androidx.room3:room3-livedata工件,并 使用@DaoReturnTypeConverters(LiveDataDaoReturnTypeConverter::class)为数据库或 DAO 添加注解。 - 番石榴粉:添加
androidx.room3:room3-guava工件,并使用 为数据库或 DAO 添加注解@DaoReturnTypeConverters(GuavaDaoReturnTypeConverter::class)。
编写异步单次查询
单次查询是指仅执行一次并在执行时获取数据快照的数据库操作。以下是异步单次查询的一些示例:
@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>> }
手动跟踪数据库失效
当您需要手动构建可观察数据库操作时,可以使用
createFlow API 的 InvalidationTracker。借助此 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:指示查询是否在事务中执行。