android - 使用异步和等待从房间数据库获取数据

标签 android kotlin android-room kotlin-coroutines deferred

我正在尝试在 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 , launchgetUserFromDB 异步执行并且在您返回之前不会完成。

为了保证完成你有两个选择(如果你想使用协程)。

  1. 标记 getUserFromDB作为暂停功能。 (强烈推荐)
suspend fun getUserFromDB() {
    val profileDao = AppDatabase.getDatabase(context).getProfileDao()
    val userProfile = withContext(Dispatchers.IO) {
        profileDao.getUserProfile().await()
    }
    return userProfile
}
  1. 使用runBlocking .
fun getUserFromDB() {
    val profileDao = AppDatabase.getDatabase(context).getProfileDao()
    val userProfile = runBlocking(Dispatchers.IO) {
        profileDao.getUserProfile().await()
    }
    return userProfile
}

现在主要问题已经解决。这里有几件事打破了惯例和准则。

  1. 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
}
  1. 在 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
}
  1. 最后也是最不重要的,如果你创建一个 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/

相关文章:

android - File.listFiles 因无效的 UTF-8 字符而崩溃

android - Android 4.4 可以支持 SHA256 密码吗?

java - 膨胀类 android.support.v7.widget.CardView 时出错

android - 为 Android Room 运行测试时出现 java.lang.NoClassDefFoundError

java - 尽管使用 @NonNull 进行注释,但 Room 上使用 @NonNull 作为主键时仍出现 Android 应用程序错误

java - Android 裁剪并减小视频大小

java - Thrift 类型作为通用类型

android - Google Pay SDK 检查用户是否已添加特定卡

android - Kotlin-多个动画同时出现和/或互相重复

Android 房间 - 清除 sqlite_sequence 不起作用