使用 Kotlin 协程从内部函数调用中得到错误结果
Getting wrong result from the inner functions call with the use of Kotlin co-routines
我创建了以下函数:
suspend fun isBatteryExistsInLocal(batteryId: String): Boolean {
val count = appDatabase.userBatteriesDao().checkBatteryExists(batteryId)
if (count > 0) {
return true
} else {
return false
}
}
检查特定记录是否存在于数据库中。
checkBatteryExists 是 dao 方法如下:
@Query("SELECT COUNT(*) FROM " + DatabaseConstant.mUserBatteriesTable + " WHERE isDeleted = 0 and batteryId= :batteryId")
suspend fun checkBatteryExists(batteryId:String): Int
方法 isBatteryExistsInLocal 在我的视图模型 class.
中从下面的函数调用
fun isBatteryExistsInLocal(batteryId:String): Boolean {
var isBatteryExistsInLocal = false
scope.launch {
isBatteryExistsInLocal =batteryRepository.isBatteryExistsInLocal(batteryId)
}
return isBatteryExistsInLocal
}
上面的方法是从我的片段中调用的,如下所示:
if (viewModel.isBatteryExistsInLocal(listNotifications[adapterPosition].batteryId)) {
但是在这里,我总是得到 false 为什么?
我已经调试并检查本地数据库中是否存在记录,并且计数变量也是 returns 1.
那为什么在上面的 if 条件下得到 false?
请指导。我做错了什么。
谢谢
当您启动协程时,它是异步启动的,就像您调用接受回调的函数一样。协程排队等待启动 运行,但当前函数可能会在 return 之前完成。这个问题的解释在 this question 的答案中,尽管它们是关于 API 回调的。启动协程时出现完全相同的问题。
将挂起函数转换为可以从协程外部调用以同步获取 return 值的唯一方法是使用 runBlocking
或调用 join
在您启动的作业上。但这不是一个可接受的解决方案,因为它会阻塞您的主线程并导致卡顿或 ANR 错误。
正确的解决方案是在工作流程的更高层使用协程,这样您就可以在任何需要的地方自由使用挂起函数。例如,如果 isBatteryExistsInLocal
是您需要作为对某些按钮按下的响应的一部分,您应该在按钮的点击侦听器中启动协程,然后您的整个操作序列可以自由地包括暂停函数调用。
顺便提一下,你可以简化你的挂起函数。不用 if/else 到 return true 或 false,你可以简单地把 return count > 0
.
与您可能预期的相反,ViewModel
中的函数按以下方式执行
fun isBatteryExistsInLocal(batteryId:String): Boolean {
var isBatteryExistsInLocal = false
scope.launch {
// Everything inside will be executed async
// outer function may already have returned by the time this completes
}
return isBatteryExistsInLocal
}
解决这个问题的一种方法是将片段代码 (viewModel.isBatteryExistsInLocal(listNotifications[adapterPosition].batteryId))
放在协程中,可以这样做
lifecycleScope.launch{
(viewModel.isBatteryExistsInLocal(listNotifications[adapterPosition].batteryId))
// other code
}
并将您的 ViewModel
函数更改为暂停
suspend fun isBatteryExistsInLocal(batteryId:String): Boolean {
return batteryRepository.isBatteryExistsInLocal(batteryId)
}
我创建了以下函数:
suspend fun isBatteryExistsInLocal(batteryId: String): Boolean {
val count = appDatabase.userBatteriesDao().checkBatteryExists(batteryId)
if (count > 0) {
return true
} else {
return false
}
}
检查特定记录是否存在于数据库中。
checkBatteryExists 是 dao 方法如下:
@Query("SELECT COUNT(*) FROM " + DatabaseConstant.mUserBatteriesTable + " WHERE isDeleted = 0 and batteryId= :batteryId")
suspend fun checkBatteryExists(batteryId:String): Int
方法 isBatteryExistsInLocal 在我的视图模型 class.
中从下面的函数调用fun isBatteryExistsInLocal(batteryId:String): Boolean {
var isBatteryExistsInLocal = false
scope.launch {
isBatteryExistsInLocal =batteryRepository.isBatteryExistsInLocal(batteryId)
}
return isBatteryExistsInLocal
}
上面的方法是从我的片段中调用的,如下所示:
if (viewModel.isBatteryExistsInLocal(listNotifications[adapterPosition].batteryId)) {
但是在这里,我总是得到 false 为什么?
我已经调试并检查本地数据库中是否存在记录,并且计数变量也是 returns 1.
那为什么在上面的 if 条件下得到 false?
请指导。我做错了什么。 谢谢
当您启动协程时,它是异步启动的,就像您调用接受回调的函数一样。协程排队等待启动 运行,但当前函数可能会在 return 之前完成。这个问题的解释在 this question 的答案中,尽管它们是关于 API 回调的。启动协程时出现完全相同的问题。
将挂起函数转换为可以从协程外部调用以同步获取 return 值的唯一方法是使用 runBlocking
或调用 join
在您启动的作业上。但这不是一个可接受的解决方案,因为它会阻塞您的主线程并导致卡顿或 ANR 错误。
正确的解决方案是在工作流程的更高层使用协程,这样您就可以在任何需要的地方自由使用挂起函数。例如,如果 isBatteryExistsInLocal
是您需要作为对某些按钮按下的响应的一部分,您应该在按钮的点击侦听器中启动协程,然后您的整个操作序列可以自由地包括暂停函数调用。
顺便提一下,你可以简化你的挂起函数。不用 if/else 到 return true 或 false,你可以简单地把 return count > 0
.
与您可能预期的相反,ViewModel
中的函数按以下方式执行
fun isBatteryExistsInLocal(batteryId:String): Boolean {
var isBatteryExistsInLocal = false
scope.launch {
// Everything inside will be executed async
// outer function may already have returned by the time this completes
}
return isBatteryExistsInLocal
}
解决这个问题的一种方法是将片段代码 (viewModel.isBatteryExistsInLocal(listNotifications[adapterPosition].batteryId))
放在协程中,可以这样做
lifecycleScope.launch{
(viewModel.isBatteryExistsInLocal(listNotifications[adapterPosition].batteryId))
// other code
}
并将您的 ViewModel
函数更改为暂停
suspend fun isBatteryExistsInLocal(batteryId:String): Boolean {
return batteryRepository.isBatteryExistsInLocal(batteryId)
}