Write asynchronous DAO queries

To prevent queries from blocking the UI, Room does not allow database access on the main thread. This restriction means that you must make your DAO queries asynchronous. The Room library includes integrations with several different frameworks to provide asynchronous query execution.

DAO queries fall into three categories:

  • One-shot write queries that insert, update, or delete data in the database.
  • One-shot read queries that read data from your database only once and return a result with the snapshot of the database at that time.
  • Observable read queries that read data from your database every time the underlying database tables change and emit new values to reflect those changes.

Language and framework options

Room provides integration support for interoperability with specific language features and libraries. The following table shows applicable return types based on query type and framework:

Query type Kotlin language features RxJava Guava Jetpack Lifecycle
One-shot write Coroutines (suspend) Single<T>, Maybe<T>, Completable ListenableFuture<T> N/A
One-shot read Coroutines (suspend) Single<T>, Maybe<T> ListenableFuture<T> N/A
Observable read Flow<T> Flowable<T>, Publisher<T>, Observable<T> N/A LiveData<T>

This guide demonstrates three possible ways that you can use these integrations to implement asynchronous queries in your DAOs.

Kotlin with Flow and couroutines

Kotlin provides language features that allow you to write asynchronous queries without third-party frameworks:

  • In Room 2.2 and higher, you can use Kotlin's Flow functionality to write observable queries.
  • In Room 2.1 and higher, you can use the suspend keyword to make your DAO queries asynchronous using Kotlin coroutines.

Java with RxJava

If your app uses the Java programming language, you can use specialized return types from the RxJava framework to write asynchronous DAO methods. Room provides support for the following RxJava 2 return types:

Additionally, Room 2.3 and higher supports RxJava 3.

Java with LiveData and Guava

If your app uses the Java programming language and you do not want to use the RxJava framework, you can use the following alternatives to write asynchronous queries:

  • You can use the LiveData wrapper class from Jetpack to write asynchronous observable queries.
  • You can use the ListenableFuture<T> wrapper from Guava to write asynchronous one-shot queries.

Write asynchronous one-shot queries

One-shot queries are database operations that only run once and grab a snapshot of data at the time of execution. Here are some examples of asynchronous one-shot queries:

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

Write observable queries

Observable queries are read operations that emit new values whenever there are changes to any of the tables that are referenced by the query. One way you might use this is to help you keep a displayed list of items up to date as the items in the underlying database are inserted, updated, or removed. Here are some examples of observable queries:

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

Additional resources

To learn more about asynchronous DAO queries, see the following additional resources:

Blogs