5
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()
    } 
}

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

4

3 回答 3

6

当你写这个:

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) { ... }

launch是一个扩展函数CoroutineScope,它将使用它coroutineContext作为起点,将它与您在括号中指定的任何内容结合起来。因此,在您的情况下,有效的上下文是

job + Dispatchers.Main + Dispatchers.Main

可以想象,这等于

job + Dispatchers.Main

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

那么 Dispatchers.Main 的原因是什么?

提供Dispatchers.Mainin的好处coroutineContext是你不必每次都提供,所以你可以只写

launch { ... }

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

为什么 Dispatchers.IO 也没有添加?

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

在另一个层面上,CoroutineContext它不是一个列表(这是由+操作员暗示的),而是一个地图。该+语法有效,因为您添加的每个对象都声明了自己的映射键,+用于将其放入上下文的内部映射中。所以实际上不可能将两个调度程序合二为一CoroutineContext

于 2018-11-16T10:04:59.250 回答
0

从 Kotlin 1.3 开始,您需要CoroutineScope一个launch新的协程。在您的示例中,您创建了一个val活动范围:

override val coroutineContext: CoroutineContext 
    get() = job + Dispatchers.Main

协程范围由不同的部分组成,例如调度程序和作业。调度程序用于启动协程 - 选择线程 - 作业用作从此范围创建的协程的父级。

如果您在调用launch方法时指定另一个调度程序,则此调度程序将覆盖标准调度程序:

launch(Dispatchers.Main) 

在您的示例中,给定Dispatchers.Main的内容会覆盖标准Dispatchers.Main- 没有任何反应。

通常,您定义一个在活动的大多数地方使用的标准调度程序,并且仅在需要时指定一个特殊的调度程序。

于 2018-11-15T17:28:11.300 回答
0

首先我不是协程专家:

第一个问题:我不明白为什么需要第 4 行中的 + Dispatchers.Main 。如果我删除这部分,那么如果 Activity 被销毁,启动协程无论如何都会被取消。那么 Dispatchers.Main 的原因是什么?

你有一个与活动 lifeCycleDispatchers.Main相关联的Job ,它与Android Main 线程调度程序相关联并使用 UI 对象进行操作

看起来很整洁。如果您的活动正在破坏,您将取消您的工作,并且如果您的主线程结束(例如发生异常),您将取消您的工作。

第二个问题:为什么也没有添加 Dispatchers.IO?

在这种情况下,更改为应用程序主线程上的另一个线程是没有意义的,因为活动位于主线程中

于 2018-11-15T17:45:45.537 回答