编写异步 DAO 查询

为了防止查询阻塞 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 中实现异步查询。

结合 Flow 和协程使用 Kotlin

Kotlin 提供了语言特性,允许您在没有第三方框架的情况下编写异步查询

  • 在 Room 2.2 及更高版本中,您可以使用 Kotlin 的 Flow 功能来编写可观察查询。
  • 在 Room 2.1 及更高版本中,您可以使用 suspend 关键字通过 Kotlin 协程使您的 DAO 查询异步化。

结合 RxJava 使用 Java

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

  • 对于一次性查询,Room 2.1 及更高版本支持 CompletableSingle<T>Maybe<T> 返回类型。
  • 对于可观察查询,Room 支持 Publisher<T>Flowable<T>注意: 要在 Room 中使用 RxJava,您必须在 build.gradle 文件中包含 room-rxjava2 工件或 room-rxjava3 工件。有关更多信息,请参阅声明依赖项

    结合 LiveData 和 Guava 使用 Java

    如果您的应用使用 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 查询,请参阅以下其他资源

    博客