我需要发出请求并等待它完成才能将其值分配给变量。 像这样
nameClient = Repository(applicationContext).getClient().toString()
fun getClient(): String? {
var nameClient: String? = "unknown"
callsApiScope.launch {
val deferred = async(Dispatchers.IO) {
callsApi.getClient(GetClientDTO(phonenumberString))
}
val response = deferred.await()
val body = response.body()
...
nameClient = nameObject.getString("fullName")
}
return nameClient?: nameClient
}
fun getClient(): String? {
var nameClient: String? = null
runBlocking {
val job = callsApiScope.launch {
val deferred = async(Dispatchers.Main) {
callsApi.getClient(GetClientDTO("+phonenumberString"))
}
val response = deferred.await()
val body = response.body()
...
nameClient = nameObject.getString("fullName")
}
}
job.join()
}
return nameClient
}
如何等待并返回值?
您首先需要决定函数何时返回。您通常有两种选择:
异步调用结果准备好后返回。 (例如,如果 api 需要 10 秒才能返回响应,则该函数将在 10 秒后返回。调用者将被阻塞 10 秒。)
在结果准备好之前返回。 (例如,即使 api 需要 10 秒才能返回响应,也要立即返回。)
选项 1 可以返回我们需要的响应,因为我们给了它足够的时间。然而,在选项2中,它不可能返回我们需要的响应,因为我们没有给它足够的时间。因此,对于选项 2,该函数必须返回某种“未来”结果。目前还没有,但将来会的。
如果你熟悉JS中
Promise
或java.util.concurrent.Future
的概念,那就是选项2应该返回的内容。
您在这里感到困惑的是,在第一个变体中,您希望代码立即返回,但您想要确切的结果。这是不可能的。你要么必须给它足够的时间,要么得到“未来”的结果。 (在协程中,“未来”结果称为
Deferred
。)
在第一个变体中,您启动了一项作业并返回了值,但没有给出作业结束的时间。因此总是返回默认值。
在第二个变体中,您确实通过在
runBlocking
块中调用挂起函数,为函数提供了足够的时间。这是一个正确的方法,通常应该有效,但我想这里的错误如下:(我可能是错的)
val deferred = async(Dispatchers.Main) {
callsApi.getClient(GetClientDTO("+phonenumberString"))
}
如果您从
Dispatchers.Main
调用此代码,则此作业将在当前代码运行后安排。但是,由于您已经处于 Dispatchers.Main
状态,并且您正在使用 deferred.await()
阻塞线程,因此实际上造成了死锁。
您的
callsApi
可以在deferred.await
完成后运行,但deferred.await
只有在callsApi
运行时才会完成。陷入了僵局。
相反,由于您已经致电
runBlocking
,因此只需拨打 val response = callsApi.getClient(GetClientDTO("+phonenumberString"))
即可。这将阻塞当前协程范围,直到获取结果为止,并将提供您需要的结果,而不会造成死锁。
但请记住,正如我在本答案开头所写的那样,您确实有两个选择。 1. 阻塞,2. 非阻塞。一般来说,如果您从主线程或不可阻塞线程调用,则应避免阻塞调用。
我通常做的是,从主线程在后台启动一个长时间运行的作业,当作业完成时,我切换回主线程。 (如果不需要,我不会切换回主线程。)
以下是事情的一般进展情况。 (对于活动,请使用
lifecycleScope
代替 viewLifecycleOwner.lifecycleScope
)
viewLifecycleOwner.lifecycleScope.launch {
val result = withContext(Dispatchers.IO) {
longRunningJob()
// do non-UI job with the result
}
withContext(Dispatchers.Main) {
// do UI job with the result
}
}
(请注意,一般建议注入调度程序)
您可能应该阅读更多有关协程如何工作以及在 Android 中使用协程的最佳实践的文章。从以下资源开始:
如果您遇到困难,也可以联系官方的人员。 https://slack-chats.kotlinlang.org/ 对于其他 Kotlin 社区,请参阅 https://kotlinlang.org/community/
愿您与协程和 Android 的旅程愉快、愉快、压力较小。