Kotlin中的协程生命周期分析

166 阅读4分钟

协程是一种并发设计模式,可以在Android平台上使用它来简化异步执行的代码,以同步的方式去编写异步执行的代码。
协程通过将复杂性放入库来简化异步编程。程序的逻辑可以在协程中顺序地表达,而底层库会为我们解决其异步性。该库可以将用户代码的相关部分包装为回调、订阅相关事件、在不同线程(甚至不同机器!)上调度执行,而代码则保持如同顺序执行一样简单。
协程的生命周期是指协程从创建到销毁的整个过程,它涵盖了协程的不同状态和阶段。
1,协程的生命周期:
New, Active, Completing, Cancelling, Cancelled, Completed.


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

新建(New):
协程被创建,但还没有开始执行。
在使用CoroutineStart.LAZY模式创建协程时,协程会处于此状态。
当指定协程的启动模式为LAZY时,在launch方法中会创建一个类型为LazyStandaloneCoroutine的对象,LazyStandaloneCoroutine类的代码如下:

private class LazyStandaloneCoroutine(
    parentContext: CoroutineContext,
    block: suspend CoroutineScope.() -> Unit
) : StandaloneCoroutine(parentContext, active = false) {
    // 创建协程
    private val continuation = block.createCoroutineUnintercepted(this, this)
    
    // 监听start方法调用
    override fun onStart() {
        // 启动协程
        continuation.startCoroutineCancellable(this)
    }
}

LazyStandaloneCoroutine类内部只对协程进行了创建,但并没有启动,只有当手动去调用start方法后才会启动。
而调用LazyStandaloneCoroutine类的start方法实际调用的是父类JobSupport的start方法,代码如下:

public final override fun start(): Boolean {
    // 循环处理状态变化
    loopOnState { state ->
        when (startInternal(state)) {
            FALSE -> return false
            TRUE -> return true
        }
    }
}

private inline fun loopOnState(block: (Any?) -> Unit): Nothing {
    while (true) {
        block(state)
    }
}

活动(Active):
协程开始执行,并在遇到suspend函数时暂停和恢复。 此时,协程处于活跃状态,可以执行其定义的任务。

完成中(Completing):
协程已经执行完毕,但还需要等待其子协程执行。 此状态与活动状态类似,因为协程还没有完全结束,但它已经完成了自己的主要任务,并正在等待子协程的完成。

完成(Completed):
协程已经完全执行完毕,包括内部的子协程。 此时,协程已经不能再被执行或修改。

取消中(Cancelling):
协程正在被取消,但还没有完成取消过程。 这通常发生在父协程取消子协程时,子协程会进入此状态,等待取消操作的完成。

已取消(Cancelled):
协程已经被取消。 此时,协程将停止执行,并释放相关资源。

2,协程的状态查询
Kotlin协程通过Job对象来查询状态和操作协程。Job对象是一个接口,它定义了一些属性和方法来控制和监视协程的状态。
val job:Job = launch { }

job.isActive:标志协程是否处于激活状态。
job.isCancelled:标志协程是否执行完成。
job.isCompleted:标志协程是否处于取消状态。
job.start():只在CoroutineStart.LAZY启动模式下的协程使用,将协程从新建状态转换为活动状态。
job.cancel():取消协程,发送取消信号给协程,协程在接收到信号后会停止执行。
job.invokeOnCompletion():协程正常完成、异常完成或被取消时,都会回调此方法。可以在此方法中进行资源释放、日志记录等操作。
job.join():join方法用于挂起当前协程,在协程A中调用协程B的join方法后,协程A会等到协程B执行完毕后再恢复执行。
代码示例如下:

runBlocking {
    val job = launch(start = CoroutineStart.DEFAULT) {
        println("launch start")
        delay(100L)
        println("launch end")
    }

    // 打印协程的初始状态
    println("isActive = ${job.isActive}, isCancelled = ${job.isCancelled}, isCompleted = ${job.isCompleted}")

    // 等待协程执行完成
    delay(200L)

    // 打印协程的最终状态
    println("isActive = ${job.isActive}, isCancelled = ${job.isCancelled}, isCompleted = ${job.isCompleted}")
}

代码的输出是:
isActive = true, isCancelled = false, isCompleted = false
launch start
launch end
isActive = false, isCancelled = false, isCompleted = true
在上面的代码中,kotlin协程中launch函数是协程构建器之一,用于启动一个新的协程。这个函数的签名通常包括一个可选的start参数,该参数决定了协程体的执行时机。
start参数是CoroutineStart枚举类型的一个值,它有三个可能的选项:DEFAULT、LAZY和ATOMIC(注意:ATOMIC在较新的Kotlin协程库中已经被废弃,因为其行为可以通过其他方式实现,并且可能导致误解)。
start = CoroutineStart.DEFAULT的作用是立即启动协程。也就是说,一旦launch函数调用完成,协程体中的代码(在这个例子中是两个println调用和delay调用)就会立即开始执行。 这是CoroutineStart.DEFAULT的默认行为,因此如果你省略start参数,协程也会以同样的方式立即启动。
CoroutineStart.DEFAULT:协程在launch调用后立即开始执行。
CoroutineStart.LAZY:协程不会立即启动,而是等待直到你显式地调用它的start()方法。这对于延迟启动协程或在特定条件下启动协程很有用。
CoroutineStart.ATOMIC(已废弃):这个选项旨在确保协程的启动是原子的,即一旦开始,就不会被取消。然而,由于可能导致误解和更复杂的取消行为,它在新版本的Kotlin协程库中已经被废弃。