编写异步 DAO 查询
为防止查询阻止界面,Room 不允许在主线程上访问数据库。此限制意味着您必须将 DAO 查询设为异步。Room 库包含与多个不同框架的集成,以提供异步查询执行功能。
DAO 查询分为三类:
- 单次写入查询,用于在数据库中插入数据或者更新或删除其中的数据。
- 单次读取查询,是指仅从数据库中读取一次数据,并在执行时返回带有数据库快照的结果。
- 可观察读取查询,是指每当底层数据库表发生变化时,都会从数据库中读取数据,并发出新值来反映这些更改。
语言和框架选项
Room 可为涉及特定语言功能和库的互操作性提供集成支持。下表根据查询类型和框架展示了适用的返回值类型:
| 查询类型 | Kotlin 语言功能 | RxJava | Guava | 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 2.2 及更高版本中,您可以使用 Kotlin 的
Flow功能编写可观察查询。 - 在 Room 2.1 及更高版本中,您可以使用
suspend关键字,通过 Kotlin 协程将 DAO 查询设为异步。
⭐ 注意
如需将 Kotlin Flow 和协程与 Room 搭配使用,您必须在 build.gradle 文件中添加 room-ktx 工件。 如需了解详情,请参阅声明依赖项。
Java 与 RxJava
如果您的应用使用 Java 编程语言,则您可以使用 RxJava 框架的专用返回类型编写异步 DAO 方法。Room 支持以下 RxJava 2 返回值类型:
- 对于单次查询,Room 2.1 及更高版本支持
Completable、Single<T>和Maybe<T>返回值类型。 - 对于可观察查询,Room 支持
Publisher<T>、Flowable<T>和Observable<T>返回值类型。
此外,Room 2.3 及更高版本支持 RxJava 3。
⭐ 注意
如需将 RxJava 与 Room 搭配使用,,您必须在 build.gradle 文件中添加 room-rxjava2 工件或 room-rxjava3 工件。 如需了解详情,请参阅声明依赖项。
Java 与 LiveData 和 Guava
如果您的应用使用 Java 编程语言,并且您不想使用 RxJava 框架,则可以使用以下替代方案来编写异步查询:
- 您可以使用 Jetpack 中的
LiveData封装容器类编写异步可观察查询。 - 您可以使用 Guava 中的
ListenableFuture<T>封装容器编写异步单次查询。
⭐ 注意
如需将 Guava 与 Room 搭配使用,,您必须在 build.gradle 文件中添加 room-guava 工件。 如需了解详情,请参阅声明依赖项。
编写异步单次查询
单次查询是指仅执行一次并在执行时获取数据快照的数据库操作。以下是异步单次查询的一些示例:
@Dao
interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUsers(vararg users: User)
@Update
suspend fun updateUsers(vararg users: User)
@Delete
suspend fun deleteUsers(vararg users: User)
@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
public interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
public Completable insertUsers(List<User> users);
@Update
public Completable updateUsers(List<User> users);
@Delete
public Completable deleteUsers(List<User> users);
@Query("SELECT * FROM user WHERE id = :id")
public Single<User> loadUserById(int id);
@Query("SELECT * from user WHERE region IN (:regions)")
public Single<List<User>> loadUsersByRegion(List<String> regions);
}@Dao
public interface UserDao {
// Returns the number of users inserted.
@Insert(onConflict = OnConflictStrategy.REPLACE)
public ListenableFuture<Integer> insertUsers(List<User> users);
// Returns the number of users updated.
@Update
public ListenableFuture<Integer> updateUsers(List<User> users);
// Returns the number of users deleted.
@Delete
public ListenableFuture<Integer> deleteUsers(List<User> users);
@Query("SELECT * FROM user WHERE id = :id")
public ListenableFuture<User> loadUserById(int id);
@Query("SELECT * from user WHERE region IN (:regions)")
public ListenableFuture<List<User>> loadUsersByRegion(List<String> regions);
}编写可观察查询
可观察查询是指在查询引用的任何表发生更改时发出新值的读取操作。您可能需要用到可观察查询的一种情形是,帮助您在向底层数据库中插入项或者更新或移除其中的项时及时更新显示的列表项。 下面是可观察查询的一些示例:
@Dao
interface UserDao {
@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>>
}@Dao
public interface UserDao {
@Query("SELECT * FROM user WHERE id = :id")
public Flowable<User> loadUserById(int id);
@Query("SELECT * from user WHERE region IN (:regions)")
public Flowable<List<User>> loadUsersByRegion(List<String> regions);
}@Dao
public interface UserDao {
@Query("SELECT * FROM user WHERE id = :id")
public LiveData<User> loadUserById(int id);
@Query("SELECT * from user WHERE region IN (:regions)")
public LiveData<List<User>> loadUsersByRegion(List<String> regions);
}⭐ 注意
Room 中的可观察查询有一项重要限制: 只要对表中的任何行进行更新(无论该行是否在结果集中),查询就会重新运行。 通过应用相应库(Flow、RxJava 或 LiveData)中的 distinctUntilChanged() 运算符,可以确保仅在实际查询结果发生更改时通知界面。