编写异步 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 查询设为异步。

Java 与 RxJava

如果您的应用使用 Java 编程语言,则您可以使用 RxJava 框架的专用返回类型编写异步 DAO 方法。Room 支持以下 RxJava 2 返回值类型:

此外,Room 2.3 及更高版本支持 RxJava 3。

Java 与 LiveData 和 Guava

如果您的应用使用 Java 编程语言,并且您不想使用 RxJava 框架,则可以使用以下替代方案来编写异步查询:

  • 您可以使用 Jetpack 中的 LiveData 封装容器类编写异步可观察查询。
  • 您可以使用 Guava 中的 ListenableFuture<T> 封装容器编写异步单次查询。

编写异步单次查询

单次查询是指仅执行一次并在执行时获取数据快照的数据库操作。以下是异步单次查询的一些示例:

Kotlin

@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>
}

Java

@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);
}

Java

@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);
}

编写可观察查询

可观察查询是指在查询引用的任何表发生更改时发出新值的读取操作。您可能需要用到可观察查询的一种情形是,帮助您在向底层数据库中插入项或者更新或移除其中的项时及时更新显示的列表项。下面是可观察查询的一些示例:

Kotlin

@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>>
}

Java

@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);
}

Java

@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);
}

其他资源

如需详细了解异步 DAO 查询,请参阅下面列出的其他资源:

博客