abstract class ScopedAppActivity: AppCompatActivity(), CoroutineScope {
    protected lateinit var job: Job
    override val coroutineContext: CoroutineContext
        get() = job + Dispatchers.Main

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

        launch(Dispatchers.Main) {
            try {
                delay(Long.MAX_VALUE)
            } catch (e: Exception) {
                // e will be a JobCancellationException if the activty is destroyed
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        job.cancel()
    }
}

此示例从 coroutine guide 复制并由 launch(Dispatchers.Main) 协程扩展。我不明白为什么需要第 4 行中的 + Dispatchers.Main。如果我删除这部分 launch 协程,如果 Activity 被销毁,无论如何都会取消。那么 Dispatchers.Main 的原因是什么?为什么也没有添加 Dispatchers.IO

最佳答案

当你写这个:

launch(Dispatchers.Main) {
    try {
        delay(Long.MAX_VALUE)
    } catch (e: Exception) {
        // e will be a JobCancellationException if the activty is destroyed
    }
}

你可能没有意识到 launch 实际上是用你的 ScopedAppActivity 作为接收者调用的。所以你有效地写了
this.launch(Dispatchers.Main) { ... }
launchCoroutineScope 上的一个扩展函数,它将使用它的 coroutineContext 作为起点,将它与您在括号中指定的任何内容结合起来。所以,在你的情况下,有效的上下文是
job + Dispatchers.Main + Dispatchers.Main

可以想象,这等于
job + Dispatchers.Main

因此,当您从 Dispatchers.Main 中删除 coroutineContext 时,没有任何变化。



Dispatchers.Main中提供coroutineContext的好处是不用每次都提供,直接写
launch { ... }

并且 launch 中的块将停留在 GUI 线程上,这是在 Android 和其他 GUI 应用程序上使用协程最自然的方式。



由于该行不是关于声明您将使用的所有调度程序,而是默认调度程序,因此提供多个调度程序是没有意义的。

在另一个层面上,CoroutineContext 不是一个列表(这是 + 操作符隐含的),而是一个映射。 + 语法有效,因为您添加的每个对象都声明了自己的映射键,+ 使用该键将其放入上下文的内部映射中。所以实际上不可能将两个调度器放在一个 CoroutineContext 中。

关于android - 为什么必须将 "Dispatchers.Main"添加到 Activity CoroutineScope 实现的根作业中?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53324408/

10-09 04:16