我正在尝试在 Coroutine Scope
中使用 async & await
从 Room Database 获取数据,但在返回值时出现问题。
这是我的代码:
fun getUserFromDB():Profile {
val profileDao = AppDatabase.getDatabase(context).getProfileDao()
CoroutineScope(Dispatchers.IO).launch {
return profileDao.getUserProfile().await()
}
}
道:
@Query("SELECT * FROM PROFILE LIMIT 1")
suspend fun getUserProfile():Deferred<Profile>
这里我想从该方法返回 userProfile,但我不能在范围内这样做,如果我从 协程范围
外返回,它将为 null。
注意:我这里没有遵循 MVVM 模式,而是做了一个简单的例子。
最佳答案
这里有一些错误,但我将首先解决主要问题。
fun getUserFromDB() {
val profileDao = AppDatabase.getDatabase(context).getProfileDao()
CoroutineScope(Dispatchers.IO).launch {
val userProfile = profileDao.getUserProfile().await()
}
return userProfile
}
在getUserFromDB
, launch
从 getUserFromDB
异步执行并且在您返回之前不会完成。
为了保证完成你有两个选择(如果你想使用协程)。
- 标记
getUserFromDB
作为暂停功能。 (强烈推荐)
suspend fun getUserFromDB() {
val profileDao = AppDatabase.getDatabase(context).getProfileDao()
val userProfile = withContext(Dispatchers.IO) {
profileDao.getUserProfile().await()
}
return userProfile
}
- 使用
runBlocking
.
fun getUserFromDB() {
val profileDao = AppDatabase.getDatabase(context).getProfileDao()
val userProfile = runBlocking(Dispatchers.IO) {
profileDao.getUserProfile().await()
}
return userProfile
}
现在主要问题已经解决。这里有几件事打破了惯例和准则。
-
getUserProfile
不应返回Deferred<Profile>
(特别是如果它已经挂起),因为这是传递的不安全原语,它应该只返回Profile
.这样您就不会意外引入不需要的并发,也不必编写await()
.
@Query("SELECT * FROM PROFILE LIMIT 1")
suspend fun getUserProfile(): Profile
fun getUserFromDB() {
val profileDao = AppDatabase.getDatabase(context).getProfileDao()
CoroutineScope(Dispatchers.IO).launch {
val userProfile = profileDao.getUserProfile() /* .await() */
}
return userProfile
}
- 在 Room 中使用协程时,它会在专用线程池中执行阻塞调用,因此您不必使用一个 (
Dispatchers.IO
),在主线程/调度程序中调用是安全的。此答案中的更多信息 Why does the querySkuDetails need to run in IO context? .
fun getUserFromDB() {
val profileDao = AppDatabase.getDatabase(context).getProfileDao()
CoroutineScope(/* Dispatchers.IO */).launch {
val userProfile = profileDao.getUserProfile().await()
}
return userProfile
}
- 最后也是最不重要的,如果你创建一个
CoroutineScope
如果以后不取消它,您应该只使用GlobalScope
以减少分配的数量。人们说“避免使用GlobalScope
。”,这是事实,但您同样应该避免创建CoroutineScope
。没有取消它们(我认为情况更糟)。
fun getUserFromDB() {
val profileDao = AppDatabase.getDatabase(context).getProfileDao()
GlobalScope.launch(Dispatchers.IO) {
val userProfile = profileDao.getUserProfile().await()
}
return userProfile
}
长话短说
我已经组织了我的答案,以便您可以在准备好时独立应用每个更改。 如果您只想要所有更改的总和,那么就在这里。
@Query("SELECT * FROM PROFILE LIMIT 1")
suspend fun getUserProfile(): Profile
suspend fun getUserFromDB(): Profile {
val profileDao = AppDatabase.getDatabase(context).getProfileDao()
val userProfile = profileDao.getUserProfile()
return userProfile
}
快乐的编码。 :)
关于android - 使用异步和等待从房间数据库获取数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62553769/