编写异步 DAO 查询

为了防止查询阻塞 UI,Room 不允许在主线程上访问数据库。此限制意味着您必须使DAO 查询异步。Room 库包含与几个不同框架的集成,以提供异步查询执行。

DAO 查询分为三类

  • 一次性写入查询,用于在数据库中插入、更新或删除数据。
  • 一次性读取查询,仅从您的数据库中读取一次数据,并返回该时间点数据库快照的结果。
  • 可观察读取查询,每当底层数据库表发生更改时,都会从您的数据库中读取数据,并发出新值以反映这些更改。

语言和框架选项

Room 提供与特定语言功能和库的互操作性的集成支持。下表显示了基于查询类型和框架的适用返回类型

查询类型 Kotlin 语言特性 RxJava Guava Jetpack Lifecycle
一次性写入 协程 (suspend) Single<T>, Maybe<T>, Completable ListenableFuture<T> N/A
一次性读取 协程 (suspend) Single<T>, Maybe<T> ListenableFuture<T> N/A
可观察读取 Flow<T> Flowable<T>, Publisher<T>, Observable<T> N/A LiveData<T>

本指南演示了三种可以在 DAO 中使用这些集成来实现异步查询的方法。

使用 Flow 和协程的 Kotlin

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 查询的更多信息,请参阅以下其他资源

示例

博客