跳至主要內容

Jobs

guodongAndroid大约 4 分钟

分析 Job

如果您注意到,协程中的启动和许多概念指的是您创建和运行的 Job。您可以用 Job 做什么呢?

当您启动协程时,您基本会要求系统执行您使用 lambda 表达式传入的代码。但是该代码不会立即执行,而是插入到队列中。

Job 是队列中协程的句柄。它只有几个字段和功能,但它提供了很多可扩展。例如,可以使用 join 在不同的 Job 实例之间引入依赖关系。如果 Job A 在 Job B 上调用 join,则意味着前者在后者完成之前不会执行。也可以使用特定的协程构建器在 Job 实例之间建立父子关系。如果所有子项都未完成,则 Job 无法完成。Job 必须完成才能使其父级完成。

Job 通过定义抽象的状态使上诉情况成为可能,其转换遵循下图描述的工作流程:

Job state flow diagram

当您启动一个协程时会创建一个 Job,此时它处于 New 状态。然后默认情况下它会直接进入 Active 状态,除非您在使用的协程构建器中提供了 LAZY CoroutineStart 参数。您还可以使用 start 或 join 将 Job 从 New 状态流转到 Active 状态。运行中的协程总是处于 Active 状态。正如前面的状态流程图所示,Job 最后会处于完成或者取消状态。

了解 Job 实例如何完成或取消工作的原理是至关重要的。特别是,直到其所有子项完成前,您可以看到 Job 一直处于 Completing 状态。重要的是,Completing 状态是内部的,如果从外部查询,Job 将返回 Active 状态。

状态是比较重要的,因为它们为您提供有关协程发生了什么以及您可以使用它们做什么的信息。 您还可以查询 Job 的状态或简单地遍历子项并对它们做一些事情。

创建作业非常简单,嵌套也不难。在 Main.kt 中的代码片段中,您隐式地创建并从启动返回了一个 Job,即使您没有使用它的引用。

此外,在图表中,您可以看到工作完成时的状态,但在取消或错误的情况下会发生什么呢??

取消 Job

当您启动一个协程并且构建器创建 Job 时,可能会发生很多事情。由于应用程序中的一些其他情况,可能会发生异常,或者您可能需要取消 Job。想象一下您在浏览图片列表,并从网络上下载图片。当您每次需要显示图片时就会启动一个协程去下载图片。下载过程中可能因为网络没有连接而导致失败,而您必须处理相关的异常。或者由于用户在当前图片显示出来前把图片滚动出屏幕之外而导致取消下载。在使用协程时,了解如何处理上述情况非常重要。

通常,未捕获的异常会导致整个程序崩溃。但是,由于协程具有挂起行为,如果发生异常,也可以挂起并稍后对移除进行处理。

有一种更简单的方法来处理取消。您可以调用对应 Job 的 cancel 函数。然后系统可以推断出 Jobs 之间的依赖关系。如果您取消一个 Job,那么它的子 Job 也会自动取消。如果它有父级,父级也会取消。如果其中一个子 Job 失败,则 Job 的父级也会被取消。

提示

注意:可以使用特殊的父 Job,它不需要所有子 Job 都完成——它们可以被单独取消或失败。 这个版本的 Job 称为 SupervisorJob。

如前所述,即使您取消了 Job ,您的代码也可能无法与取消事件达成合作[1]。您可以使用它的 isActive 属性来检查这一点。如果您的代码执行计算工作,而不检查 isActive 标志,则它不会监听取消事件。因此,使用 isActive 标志运行 while 循环比使用您自己的条件更安全。或者你至少应该尝试在你的条件之上依赖 isActive。


  1. 协程取消与线程停止都是协作式的。 ↩︎