我试图让我了解suspendCoroutine
和suspendCancellableCoroutine
。我认为它们在以下情况下可能很有用:
这样可以编译,但绝不会使其超出“延迟时间”,即,延续不会恢复:
import kotlinx.coroutines.*
fun main(args: Array<String>) {
println("Hello, world!")
runBlocking {
launch {
postComment()
}
}
}
var isLoggedIn = false
var loginContinuation: CancellableContinuation<Unit>? = null
suspend fun postComment() {
if (!isLoggedIn) {
showLoginForm()
suspendCancellableCoroutine<Unit> {
loginContinuation = it
}
}
// call the api or whatever
delay(1000)
println("comment posted!")
}
suspend fun showLoginForm() {
println("show login form")
// simulate delay while user enters credentials
delay(1000)
println("delay over")
isLoggedIn = true
// resume coroutine on submit
loginContinuation?.resume(Unit) { println("login cancelled") }
}
我已经尝试了所有我能想到的一切,包括将调用移至登录检查之外的
suspendCancellableCoroutine
,将showLoginForm
的内容包装在withContext(Dispatchers.IO)
中,使用coroutineScope.launch(newSingleThreadContext("MyOwnThread")
等。通过阅读互联网,我得到的印象是这是一种有效的用法案件。我究竟做错了什么? 最佳答案
绕过Continuations很杂乱,很容易导致您遇到错误...一个函数甚至在将continuation分配给continuation属性之前就完成了。
由于您想将登录表单转换为暂停函数,因此应该在其中使用suspendCoroutine
。 suspendCoroutine
是您应该放低的低级代码,这样您的主程序逻辑可以使用易于读取的顺序协程,而无需嵌套的launch
/ suspendCoroutine
调用。
var isLoggedIn = false
suspend fun postComment() {
if (!isLoggedIn) {
showLoginForm()
}
println("is logged in: $isLoggedIn")
if (isLoggedIn) {
// call the api or whatever
delay(1000)
println("comment posted!")
}
}
suspend fun showLoginForm(): Unit = suspendCancellableCoroutine { cont ->
println("Login or leave blank to cancel:")
//Simulate user login or cancel with console input
val userInput = readLine()
isLoggedIn = !userInput.isNullOrBlank()
cont.resume(Unit)
}
我没有在
delay()
中使用showLoginForm()
,因为您无法在suspendCancellableCoroutine
块中调用暂停函数。最后三行也可以包装在scope.launch
中,并使用delay
而不是readLine
,但是实际上,您的UI交互总不是一个协程,不会有延迟。编辑:
试图将延续传递给另一个Activity会特别麻烦。 Google甚至不建议在一个应用程序中使用多个 Activity ,因为很难在它们之间传递对象。要使用Fragments进行操作,您可以编写LoginFragment类以具有如下这样的私有(private)延续属性:
class LoginFragment(): Fragment {
private val continuation: Continuation<Boolean>? = null
private var loginComplete = false
suspend fun show(manager: FragmentManager, @IdRes containerViewId: Int, tag: String? = null): Boolean = suspendCancelableCoroutine { cont ->
continuation = cont
retainInstance = true
manager.beginTransaction().apply {
replace(containerViewId, this@LoginFragment, tag)
addToBackStack(null)
commit()
}
}
// Call this when login is complete:
private fun onLoginSuccessful() {
loginComplete = true
activity?.fragmentManager?.popBackStack()
}
override fun onDestroy() {
super.onDestroy()
continuation?.resume(loginComplete)
}
}
然后,您将显示来自另一个片段的该片段,如下所示:
lifecycleScope.launch {
val loggedIn = LoginFragment().show(requireActivity().fragmentManager, R.id.fragContainer)
// respond to login state here
}
只要您使用的是Fragment的
lifecycleScope
而不是Activity的lifecycleScope
,并且第一个Fragment也使用retainInstance = true
,我认为您应该可以避免屏幕旋转。但是我还没有自己做。关于asynchronous - Kotlin延续无法恢复,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/60278108/