8

弃用警报

此代码使用旧的 Coroutines Api。如果您使用的是 kotlinx-coroutines 1.1.0 或更高版本,则此代码对您没有用处

原来的问题是:

我发现我的 Android 应用程序中的这个特定代码阻止了 UI 线程:

runBlocking {
    async(CommonPool) {
        Thread.sleep(5000)     
    }.await()
}

textView.text = "Finish!"

我一直在为几个任务使用协程,它们从不阻塞 UI 线程,如可以在文档中阅读的那样:

. 协程提供了一种避免阻塞线程的方法,并用更便宜、更可控的操作来代替它:暂停协程

但奇怪的是,这段代码:

runBlocking {
    async(CommonPool) {
        launch(CommonPool) {
            Thread.sleep(5000)

            runOnUiThread { textView.text = "Finish!" }
        }
    }.await()
}

表现如预期;不阻塞,等待五秒钟然后打印结果(我需要在之后更新 UI,并且只有在sleep完成之后)

文档说可以独立使用asynclaunch不需要组合。其实async(CommonPool)应该够了。

那么这里到底发生了什么?为什么它只适用于async+launch

更新(2021 年)

[弃用警告] 此代码使用旧的 Coroutines Api。如果您使用的是 kotlinx-coroutines 1.1.0 或更高版本,请忘记这段代码

我的完整示例代码:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        button1.setOnClickListener {
            runBlocking {
                async(CommonPool) {
                    Thread.sleep(5000L)
                }.await()
            }

            textView1.text = "Finally! I've been blocked for 5s :-("
        }

        button2.setOnClickListener {
            runBlocking {
                async(CommonPool) {
                    launch(CommonPool) {
                        Thread.sleep(5000L)

                        runOnUiThread { textView1.text = "Done! UI was not blocked :-)" }
                    }
                }.await()
            }
        }
    }
}
4

2 回答 2

8

注意:这篇文章可以追溯到协程的预发布版本。我更新了调度程序的名称以匹配发布版本。

runBlocking不是在 UI 线程上启动协程的方法,因为正如其名称所说,它会阻塞宿主线程,直到协程完成。您必须launchMain上下文中对其进行切换,然后切换到Default重量级操作的上下文。您还应该删除该async-await对并使用withContext

button1.setOnClickListener {
    launch(Main) {
        withContext(Default) {
            Thread.sleep(5000L)
        }
        textView1.text = "Done! UI was not blocked :-)"
    }
}

withContext将暂停协程直到完成,然后在父上下文中恢复它,即Main.

于 2018-02-08T10:45:27.577 回答
1

正如文档告诉我们的那样:

[ runBlocking] 运行新的协程并中断当前线程直到其完成。不应从协程中使用此功能。它旨在将常规阻塞代码与以挂起样式编写的库连接起来,以便在main函数和测试中使用。

正如所引用的,它通常用于与常规协程一起工作的测试中,也main用于等待协程完成的方法中。

此外,本教程将有助于理解其用例。

于 2018-02-08T11:19:01.867 回答