Kotlin协程详解——Job

1.Job介绍

基本上每启动一个协程就会产生对应的Job,例如

lifecycleScope.launch {
}

launch返回的就是一个Job,它可以用来管理协程,一个Job中可以关联多个子Job,同时它也提供了通过外部传入parent的实现

public fun Job(parent: Job? = null): Job = JobImpl(parent)

这个很好理解,当传入parent时,此时的Job将会作为parent的子Job

2.运行状态

既然Job是来管理协程的,那么它提供了六种状态来表示协程的运行状态。

  1. New: 创建

  2. Active: 运行

  3. Completing: 已经完成等待自身的子协程

  4. Completed: 完成

  5. Cancelling: 正在进行取消或者失败

  6. Cancelled: 取消或失败

这六种状态Job对外暴露了三种状态,它们随时可以通过Job来获取

public val isActive: Boolean
public val isCompleted: Boolean
public val isCancelled: Boolean

所以如果你需要自己来手动管理协程,可以通过下面的方式来判断当前协程是否在运行。

while (job.isActive) {
// 协程运行中            
}

一般来说,协程创建的时候就处在Active状态,但也有特例。

例如我们通过launch启动协程的时候可以传递一个start参数

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
 ...
}

如果这个start传递的是CoroutineStart.LAZY,那么它将处于New状态。可以通过调用start或者join来唤起协程进入Active状态。

下面我们来看一张简图,就能很清晰的了解Job中的六个状态间的转化过程。

                                        wait children
  +-----+ start  +--------+ complete   +-------------+  finish  +-----------+
  | New | -----> | Active | ---------> | Completing  | -------> | Completed |
  +-----+        +--------+            +-------------+          +-----------+
                   |  cancel / fail       |
                   |     +----------------+
                   |     |
                   V     V
               +------------+                           finish  +-----------+
               | Cancelling | --------------------------------> | Cancelled |
               +------------+                                   +-----------+

上面已经提及到一个Job可以有多个子Job,所以一个Job的完成都必须等待它内部所有的子Job完成;对应的cancel也是一样的。

3.SupervisorJob

默认情况下,如果内部的子Job发生异常,那么它对应的parent Job与它相关连的其它子Job都将取消运行。俗称连锁反应

我们也可以改变这种默认机制,Kotlin提供了SupervisorJob来改变这种机制。这种情况还是很常见的,例如用协程请求两个接口,但并不想因为其中一个接口失败导致另外的接口也不请求,这时就可以使用SupervisorJob来改变协程的这种默认机制。

使用很简单,在我们创建CoroutineContext的时候加入SupervisorJob即可。例如在上面提到过的lifecycleScope,内部就使用到了SupervisorJob

val newScope = LifecycleCoroutineScopeImpl(
    this,
    SupervisorJob() + Dispatchers.Main
)

你也可以尝试运行下面的这个例子,然后将它的SupervisorJob替换成别的CoroutineContext再来看下效果。

fun main() = runBlocking {
    val supervisor = SupervisorJob()
    with(CoroutineScope(coroutineContext + supervisor)) {
        // 启动第一个子作业——这个示例将会忽略它的异常(不要在实践中这么做!)
        val firstChild = launch(CoroutineExceptionHandler { _, _ ->  }) {
            println("The first child is failing")
            throw AssertionError("The first child is cancelled")
        }
        // 启动第二个子作业
        val secondChild = launch {
            firstChild.join()
            // 取消了第一个子作业且没有传播给第二个子作业
            println("The first child is cancelled: ${firstChild.isCancelled}, but the second one is still active")
            try {
                delay(Long.MAX_VALUE)
            } finally {
                // 但是取消了监督的传播
                println("The second child is cancelled because the supervisor was cancelled")
            }
        }
        // 等待直到第一个子作业失败且执行完成
        firstChild.join()
        println("Cancelling the supervisor")
        supervisor.cancel()
        secondChild.join()
    }
}

如果有些任务你并不想被手动取消,可以使用NonCancellable作为任务的CoroutineContext

如果需要Job获取协程的返回结果,可以通过Deferred来实现,它是Job的一个子类,所以也拥有Job所用功能。同时额外提供await方法来等待协程结果的返回。

Deferred可以通过CoroutineScope.async创建。

最后我们再来介绍下Job的几个方法,startcancel就不多说了,分别是启动与取消。

4.invokeOnCompletion

这个方法是Job的回调通知,当Job执行完后会调用这个方法

public fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle
 
public typealias CompletionHandler = (cause: Throwable?) -> Unit

这个cause有三种情况分别为:

  1. is null: 协程正常执行完毕

  2. is CancellationException: 协程正常取消,并非异常导致的取消

  3. Otherwise: 协程发生异常

同时它的返回值DisposableHandle可以用来取消回调的监听。

5.join

public suspend fun join()

注意这是一个suspend函数,所以它只能在suspend或者coroutine中进行调用。

它的作用是暂停当前运行的协程任务,立刻执行自身Job的协程任务,直到自身执行完毕之后才恢复之前的协程任务继续执行。

推荐文章

Kotlin协程实现原理:CoroutineScope&Job

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲暇部落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值