Room & kotlin coroutine

room kotlin coroutine

Overview

Room 2.1 开始支持 Kotlin 协程。DAO 方法可以使用 suspend 标记以确保这些方法不会在主线程中被执行。
Room 使用 Executor(来自框架组件) 作为 Dispatcher 运行 SQL 语句,当然在编译 RoomDatabase 时,你也可以提供自己的 Executor.

当前协程支持正在开发中,更多特性正在计划中

添加依赖

请升级 Room 到 v2.1, 同时 Kotlin v1.3.0+ , Coroutines v1.0.0+

1
implementation "androidx.room:room-coroutines:${versions.room}"

现在就可以使用啦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Dao
interface UsersDao {

@Query("SELECT * FROM users")
suspend fun getUsers(): List<User>

@Query("UPDATE users SET age = age + 1 WHERE userId = :userId)
suspend fun incrementUserAge(userId: String)

@Insert
suspend fun insertUser(user: User)

@Update
suspend fun updateUser(user: User)

@Delete
suspend fun deleteUser(user: User)
}

在调用其他 suspending DAO 函数时,@Transaction 也可以被 suspending

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@Dao
abstract class UsersDao {
@Transaction
open suspend fun setLoggedInUser(loggedInUser: user) {
deleteUser(loggedInUser)
insertUser(loggedInUser)
}

@Query("DELETE FROM users")
abstract fun deleteUser(user: User)

@Insert
abstract suspend fun insertUser(user: User)
}

根据是否在 transaction 内调用,Room 对 suspending 函数处理逻辑不同.

  • 在 Transaction 中

    在数据库语句被触发的 CoroutineContext 下,Room 不做任何处理。函数调用者应该确保此方法不会在 UI 线程中执行.因为 suspend 函数只能被其他 suspend 函数 或在 coroutine 内调用,所以你不能把 Dispatchers.Main 赋值给 Dispatcher,应该是 Dispatchers.IO 或自定义

  • 不在 Transaction 中

    Room 会确保数据库语句在 Architecutre Components I/O Dispatcher 中触发。该 Dispatcher 在同一个 I/O Executor 的一个后台线程中运行 LiveData

底层

1
2
3
4
5
@Insert
fun insertUserSync(user: User)

@Insert
suspend fun insertUser(user: User)

对于同步 insert,生成的代码开始启动一个 transaction,然后执行 insert,标记 transaction successfull ,终结。
同步方法在被调用处的线程执行.

1
2
3
4
5
6
7
8
9
10
@Override
public void insertUserSync(final User user) {
__db.beginTransaction();
try {
__insertionAdapterOfUser.insert(user);
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}

suspending 会确保不会在 UI 线程中执行。生成的代码会传递一个 Continuation.在 Callable#call() 中执行和同步相同的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public Object insertUserSuspend(final User user,
final Continuation<? super Unit> p1) {
return CoroutinesRoom.execute(__db, new Callable<Unit>() {
@Override
public Unit call() throws Exception {
__db.beginTransaction();
try {
__insertionAdapterOfUser.insert(user);
__db.setTransactionSuccessful();
return kotlin.Unit.INSTANCE;
} finally {
__db.endTransaction();
}
}
}, p1);
}

CoroutinesRoom.execute 会根据数据库是否 open,当前调用是否在 transaction 内来切换处理 context.

  • is open & in transaction

    仅调用 insert 逻辑

  • not in transaction

    使用 Architecture Components IO Executor 在后台线程执行 insert 逻辑