我在下面有一些代码。延迟(3000)只是长循环(或循环)的替代。我希望循环println(res)
完成后会显示“ Some String”,然后启用button
。但是在现实生活中,println(res)
打印一个空字符串,并且在我单击button
的同时启用它。
我的问题是:如何等待协程结束并且仅在协程完成后才运行println(res)
和button.isEnabled = true
。
private var res: String = ""
private suspend fun test(): String {
delay(3000) // delay - just replacement for long loop
return "Some String" // String received after loop
}
fun onClick(view: View) {
res = ""
button.isEnabled = false
GlobalScope.launch {
res = withContext(Dispatchers.Default) {
test()
}
}
println(res) // 1. trying to get string received after loop, but not working
button.isEnabled = true // 2. button must be enabled after loop in cycle, but it's not waiting till end of loop
}
答案 0 :(得分:2)
为什么不在println
协程中移动button.isEnabled
和GlobalScope.launch
。
fun onClick(view: View) {
res = ""
button.isEnabled = false
GlobalScope.launch {
val res = withContext(Dispatchers.Default) {
test()
}
println(res)
button.isEnabled = true
}
}
如果您希望代码在main
线程上运行,请添加Dispatchers.Main
作为参数。
GlobalScope.launch(Dispatchers.Main) {
val res = withContext(Dispatchers.Default) {
test()
}
println(res)
button.isEnabled = true
}
现在println
和button.isEnabled
在main
线程上运行,而test()
的乐趣在Default
上运行,它实际上是一个工作线程。
答案 1 :(得分:1)
这里要了解的主要是,协程默认情况下是 顺序执行的代码。 即协程是相对于“同级”代码异步执行的,但是协程中的代码默认情况下是同步执行的。
例如:
fun DoSometing () {
coroutineA {
doSomethingA1()
doSomethingA2()
}
some additional code
}
例程A将相对于一些其他代码执行异步操作,但doSometingA2将在doSomethingA1完成后执行。
这意味着,在一个协程中,下一个代码将在上一个代码完成后执行。 因此,完成协程后要执行的任何操作,都只需放在协程的末尾并声明要在其中执行的上下文( withContext )。
如果您在协程中启动另一个异步代码段(例如另一个协程),当然是例外。
编辑:如果需要从协程更新UI,则应在主上下文中执行该操作,即,您将具有以下内容:
GlobalScope.launch (Dispatchers.IO) {
//do some background work
...
withContext (Dispatchers.Main) {
//update the UI
button.isEnabled=true
...
}
}
答案 2 :(得分:1)
您可以尝试以下操作:
suspend fun saveInDb() {
val value = GlobalScope.async {
delay(1000)
println("thread running on [${Thread.currentThread().name}]")
10
}
println("value = ${value.await()} thread running on [${Thread.currentThread().name}]")
}
await将等待协程完成,然后在其下方运行代码
fun onClick(view: View) {
res = ""
button.isEnabled = false
GlobalScope.launch(Dispatchers.main){ // launches coroutine in main thread
updateUi()
}
}
suspend fun updateUi(){
val value = GlobalScope.async { // creates worker thread
res = withContext(Dispatchers.Default) {
test()
}
}
println(value.await()) //waits for workerthread to finish
button.isEnabled = true //runs on ui thread as calling function is on Dispatchers.main
}
答案 3 :(得分:0)
在继续当前线程之前,使用Job.join(): Unit
方法等待协程完成:
//launch coroutine
var result = ""
val request = launch {
delay(500)
result = "Hello, world!"
}
//join coroutine with current thread
request.join()
//print "Hello, world!"
println(result)
答案 4 :(得分:0)
launch
用于不关心协程之外结果的情况。要检索协程的结果,请使用async
。
val res = GlobalScope.async(Dispatchers.Default) { test() }.await()
注意:避免使用GlobalScope
,而应提供自己的CoroutineScope
。