允许在 Kotlin 协程中使用主线程查询的 Room

3

官方的Android文档表示不建议使用allowMainThreadQueries(),因为它可能会长时间锁定UI并触发ANR。 但是Kotlin协程使我们能够在主线程中执行某些操作而不会有效地阻塞UI。

因此我想问:在主线程上运行的couroutine范围内使用allowMainThreadQueries()并访问数据库是否安全? 就像下面这样:

// WITH allowMainThreadQueries()
val activityJob = Job()
val mainScope = CoroutineScope(Dispatchers.Main + activityJob)
mainscope.launch {

    // access room database and retrieve some data

    // update UI with data retrived

}

或者我们应该坚持不允许在主线程查询并在另一个线程中执行数据库查询的旧方式?
// WITHOUT allowMainThreadQueries()
val activityJob = Job()
val defaultScope = CoroutineScope(Dispatchers.Default + activityJob)
val mainScope = CoroutineScope(Dispatchers.Main + activityJob)
defaultScope.launch {

    // access room database and retrieve some data

    mainScope.launch {
        // update UI with data retrived
    }

}

我问这个问题是因为前一种方式(使用 allowMainThreadQueries()):
  • 更易读(我可以在访问数据库的函数的同一协程上下文中更新UI,而无需考虑在另一个协程范围内启动UI更新)
  • 允许更简单的错误处理
  • 仅使用一个协程范围(所以需要关注的范围更少)

1
如果数据库访问方法被标记为suspend,那么您可以选择第一种方法,否则选择第二种方法。 - Sergio
2个回答

1

您不需要使用allowMainThreadQueries()来使其正常工作。作用域协程在其线程中执行。

这是我不久前所做的:

@UiThread
fun getUsers(context: Context): LiveData<List<User>> {
    if (!::users.isInitialized) {
        users = MutableLiveData()
        users.postValue(MyDatabase.get(context).users().getAll())
        GlobalScope.launch(Dispatchers.Main) {
            val usersFromDb: List<User> = async(Dispatchers.IO) {
                return@async MyDatabase.get(context).users().getAll()
            }.await()
            users.value = usersFromDb
        }
    }
    return users
}

你可以看到这个getUsers()方法从主线程调用,返回一个LiveData(在这种情况下非常方便)。数据库查询发生在GlobalScope.launch()中。
所以,是的,你的设计是我个人喜欢的。而且它也是有效的。但我认为你根本不需要使用allowMainThreadQueries()。随意阅读(我的)博客文章:https://proandroiddev.com/android-viewmodel-livedata-coroutines-contraption-e1e44af690a6

我需要使用allowMainThreadQueries(),否则将会出现运行时错误。我没有使用MutableLiveData,只是通过Dao访问数据库实体。 - Simone
1
在CoroutineScope内部,您不需要使用allowMainThreadQueries(),也不会收到任何异常,这就是我的观点。这就是我正在使用的代码。没有allowMainThreadQueries() - shkschneider
除非您在工作后明确取消,否则不建议使用全局范围。 - Willey Hute

0

建议在viewmodelScope内访问数据库。

如果您需要从活动或片段访问room数据库,请使用:

lifecyclescope.launch{ // 在此处访问暂停定义的数据库dao函数。 }

或者在lifecycleScope内使用withContext(Dispatchers.IO){}更改线程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接