一文搞懂 Kotlin Coroutine Job 的工作流程

Kotlin Coroutine 源码基于 1.5.2

读完这篇文章,你会知道什么?

  1. Job 相关类之间的作用和关系
  2. Job定义状态存储状态 的区别和联系
  3. ParentJobChildJob 的关联流程
  4. Jobcancel 流程,有完整的 结构划分注释说明
  5. Jobjoin 流程
  6. Job 的异常分发流程

官方定义

  • 一个 可取消带生命周期 的任务;对应 Job#cancelJob#isActive 方法

  • Job 是具有 父节点子节点 ,可以组成一个 多叉树 ;当 父节点 取消的时候,会递归取消自身所有的 子节点

  • 子节点 出现了「除 CancellationException 之外」异常,将立即取消其 父节点 的任务,进而取消其 父节点下的所有子节点 ,可以通过 SupervisorJob 定义此行为;同时这句话也有另外一个意思,即 子节点 产生了 CancellationException 异常(比如 Job#cancel) ,不会影响 父节点 的运行

  • Job 一般分为 Corountine JobCompletableJob 两种

    • Coroutine Job 一般通过 CoroutineScope#launch(block) 创建, 当 block 代码块执行完成,则代表当前 Job 执行完成「可能还需要等待 Child Job

    • CompletableJob 一般顶层函数 Job() 创建,当调用 CompletableJob#complete 时候代表完成,一般用于 CoroutineScope 的创建「因为当前 Job 的生命周期取决于 Scope ,由 Scope 管理 Jobcancelcomplete

    Kotlin 复制代码
    public val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
        get() = lifecycle.coroutineScope
    
    public val Lifecycle.coroutineScope: LifecycleCoroutineScope
        get() {
            while (true) {
                val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?
                if (existing != null) return existing
                
                /**
                 * 这里使用 SupervisorJob 返回 CompletableJob 实例
                 */
                val newScope = LifecycleCoroutineScopeImpl(
                    this,
                    SupervisorJob() + Dispatchers.Main.immediate
                )
                
                if (mInternalScopeRef.compareAndSet(null, newScope)) {
                    newScope.register()
                    return newScope
                }
            }
        }
        
    public fun SupervisorJob(parent: Job? = null) : CompletableJob = SupervisorJobImpl(parent)

Job 相关的类

JobSupport

classDiagram Job <|-- ParentJob Job <|-- ChildJob Job <|-- Deferred Job <|-- JobSupport Job <|-- CompletableJob ParentJob <|-- JobSupport ChildJob <|-- JobSupport JobSupport <|-- AbstractCoroutine Job <|-- AbstractCoroutine
Kotlin 复制代码
private class SupervisorJobImpl(parent: Job?) : JobImpl(parent) {
    // ...
}

internal open class JobImpl(parent: Job?) : JobSupport(true), CompletableJob {
     // ...
}

private class CompletableDeferredImpl<T>(
    parent: Job?
) : JobSupport(true), CompletableDeferred<T>, SelectClause1<T> {
    // ...
}

public interface CompletableDeferred<T> : Deferred<T> {
    // ...
}

继承图 和 「Deferred<T>CompletableJob」 派生类可知,所有的 Job 继承实例都 直接或间接 继承了 JobSupport ,同时 JobSupport 实现 ParentJobChildJob , 不仅可以充当树上的 叶子节点 也可以充当 分支结点 ,从而组成一颗 多叉树 ;所以要想了解 Job 的内容,就要了解 JobSupport , 而要了解 JobSupport 的代码,需要理清下面「类之间的关系」,不然直接看源码就会 云里雾里

ParentJob 和 ChildJob

Kotlin 复制代码
public interface ParentJob : Job {
    @InternalCoroutinesApi
    public fun getChildJobCancellationCause(): CancellationException
}

public interface ChildJob : Job {
    @InternalCoroutinesApi
    public fun parentCancelled(parentJob: ParentJob)
}

ParentJobChildJob 的接口是配合使用的

  • ParentJob#getChildJobCancellationCause 提供给 ChildJob 获取 ParentJob 自身 cancel 的原因
  • ChildJob#parentCancelled(parentJob: ParentJob) 提供 ParentJob 取消 自身ChildJob , 参数 ParentJob 就是让 ChildJob 调用 ParentJob#getChildJobCancellationCause

Tips: 由 官方定义 可得,这里的返回值是 CancellationException 异常,CancellationException 只会从 树形结构 的 「父节点」 往 「子节点」 进行分发取消;因为 初始异常 早已经分发到父级,这里只需要 递归取消所有子节点 即可

CompletionHandler

Kotlin 复制代码
public typealias CompletionHandler = (cause: Throwable?) -> Unit

internal actual abstract class CompletionHandlerBase actual constructor() : LockFreeLinkedListNode(), CompletionHandler {
    /**
     * 函数名为 invoke,函数类型为 (cause: Throwable?) -> Unit
     */
    actual abstract override fun invoke(cause: Throwable?)
}

/**
 * CompletionHandlerBase 是继承 CompletionHandler,返回自身即可
 */
internal actual inline val CompletionHandlerBase.asHandler: CompletionHandler get() = this

CompletionHandlerJob 完成的一个回调,同时也是一个函数类型, CompletionHandlerBase 继承 CompletionHandler 就会出现一个 abstract override fun invoke(cause: Throwable?) 方法需要重写,而 LockFreeLinkedListNode 是一个可在多线程操作的链表

ChildHandleNode

Kotlin 复制代码
internal class ChildHandleNode(
    @JvmField val childJob: ChildJob
) : JobCancellingNode(), ChildHandle {
    override val parent: Job get() = job
    /**
     * CompletionHandler 实现
     */
    override fun invoke(cause: Throwable?) = childJob.parentCancelled(job)
    /**
     * ChildHandle 的实现,用于取消 ParentJob 任务
     */
    override fun childCancelled(cause: Throwable): Boolean = job.childCancelled(cause)
}

// JobCancellingNode 继承树
internal abstract class JobCancellingNode : JobNode()

internal abstract class JobNode : CompletionHandlerBase(), DisposableHandle, Incomplete {
    /**
     * 存储的是 ParentJob,在 makeNode 的时候进行赋值
     */
    lateinit var job: JobSupport
    /**
     * Incomplete 的实现
     */
    override val isActive: Boolean get() = true
    override val list: NodeList? get() = null
    /**
     * DisposableHandle 的实现
     */
    override fun dispose() = job.removeNode(this)
}

// ChildHandle 继承树
public interface ChildHandle : DisposableHandle {
    public val parent: Job?

    public fun childCancelled(cause: Throwable): Boolean
}

public interface DisposableHandle {
    public fun dispose()
}

ChildHandleNode 是继承 ChildHandleJobCancellingNode

ChildHandle 有三个功能

  1. parent 字段持有 ParentJob
  2. childCancelled 用于 ChildJob 加入已完成 ParentJob 的时候,ChildJob 可以自动取消
  3. DisposableHandle 是用于回收的,从上面看可知具体由 JobNode 实现

JobCancellingNode 相比于 JobNode 而言,可以在 「Cancelling 」 状态下就收到回调,而不用等到 完全取消 才收到回调,主要有三个功能

  1. 实现 CompletionHandlerBase ,可监听 Job 完成,主要用于监听 ParentJob
  2. 实现 DisposableHandle ,用于取消对 Job 完成的监听
  3. 实现 Incomplete , 是 Job 状态的子类,用于标记 Job 是否存活(isActive) 和 当前监听列表(NodeList),其中 Job的状态 会在下面说明

Job 状态

定义状态

Job 的官方注释上状态分为 NewActiveCompletingCancellingCancelledCompleted 状态, 而在对应状态上 Job#isActiveJob#isCompletedJob#isCancelled 值分别如表格所示

State [isActive] [isCompleted] [isCancelled]
New (optional initial state) false false false
Active (default initial state) true false false
Completing (transient state) true false false
Cancelling (transient state) false false true
Cancelled (final state) false true true
Completed (final state) false true false

状态转移图

Kotlin 复制代码
                                        wait children
  +-----+ start  +--------+ complete   +-------------+  finish  +-----------+
  | New | -----> | Active | ---------> | Completing  | -------> | Completed |
  +-----+        +--------+            +-------------+          +-----------+
                   |  cancel / fail       |
                   |     +----------------+
                   |     |
                   V     V
               +------------+                           finish  +-----------+
               | Cancelling | --------------------------------> | Cancelled |
               +------------+                                   +-----------+
               wait children

New(初始状态) :设置了延迟执行, 如 launch(start = CoroutineStart.LAZY),需要调用方手动调用 Job#start 才会进入Active 状态

Active(初始状态) :如果没有设置 延迟执行 , 则在 launch 的时候就会处于这种状态;这里分为 两种 情况过渡到 Completing 状态

  1. CompletableJob 的时候,需要手动调用 CompletableJob#complete 进入 Completing(完成过渡状态),如转移图所述
  2. Coroutine Job 的时候,则会在 block 代码块 执行完成自动进入 Completing 状态

Completing/Cancelling(过渡状态) :当自身任务「已经完成」 或者 「已经取消」,等待所有的 ChildJob 「完成」 或者 「取消」

Completed/Cancelled(最终状态)Job 及其 ChildJob 任务「已经完成」 或者 「已经取消」

存储状态

上面所述的是 定义状态 ,实际上 存储状态 是有点差异,主要差异在于 listeners 的数目,而这个 listeners 就是当前监听 Job 完成个数「从后面的插入代码和分发事件源码解析可知」

Kotlin 复制代码
name       state class              public state  description
------     ------------             ------------  -----------
EMPTY_N    EmptyNew               : New           no listeners
EMPTY_A    EmptyActive            : Active        no listeners
SINGLE     JobNode                : Active        a single listener
SINGLE+    JobNode                : Active        a single listener + NodeList added as its next
LIST_N     InactiveNodeList       : New           a list of listeners (promoted once, does not got back to EmptyNew)
LIST_A     NodeList               : Active        a list of listeners (promoted once, does not got back to JobNode/EmptyActive)
COMPLETING Finishing              : Completing    has a list of listeners (promoted once from LIST_*)
CANCELLING Finishing              : Cancelling    -- " --
FINAL_C    Cancelled              : Cancelled     Cancelled (final state)
FINAL_R    <any>                  : Completed     produced some result

New(初始状态) : 对应的存储状态为 EmptyNewInactiveNodeList

  • listener 差异可知,EmptyNew 没有任何 listener , 而 InactiveNodeList 是有多个 listener
  • 从状态转移来说:一旦由 「EmptyNew」 推进到 「InactiveNodeList」 状态的时候,就不能回退「代码中移除 listener 的时候可以体现」
Kotlin 复制代码
private val EMPTY_NEW = Empty(false)
private class Empty(override val isActive: Boolean) : Incomplete {
    override val list: NodeList? get() = null
    override fun toString(): String = "Empty{${if (isActive) "Active" else "New" }}"
}

internal class InactiveNodeList(override val list: NodeList) : Incomplete {
    override val isActive: Boolean get() = false
}

Active(初始状态) :对应的存储状态为 EmptyActiveJobNodeNodeList

  • listener 差异可知, EmptyActive 没有任何 listener , JobNode 只有一个 listener ,而 NodeList 是有多个 listener
  • 从状态转移来说:一旦由 「EmptyActive 或者 JobNode 」 推进到 「NodeList」 状态的时候,就不能回退;但 「EmptyActive」和 「JobNode」 互相切换;
  • 这里比 New 状态多了个 JobNode ,因为在这里不仅是监听,同时也是链表节点,可以线程安全的插入进 Incomplete#list
Kotlin 复制代码
private val EMPTY_ACTIVE = Empty(true)

internal abstract class JobNode : CompletionHandlerBase(), DisposableHandle, Incomplete {
    lateinit var job: JobSupport
    override val isActive: Boolean get() = true
    override val list: NodeList? get() = null
    override fun dispose() = job.removeNode(this)
}

internal class NodeList : LockFreeLinkedListHead(), Incomplete {
    override val isActive: Boolean get() = true
    override val list: NodeList get() = this
}

Completing/Cancelling(过渡状态) : 对应的存储状态为 Finishing , 使用 Finishing#isCancelling 来区分 Completing 还是 Cancelling

Kotlin 复制代码
private class Finishing(
        override val list: NodeList,
        isCompleting: Boolean,
        rootCause: Throwable?
) : SynchronizedObject(), Incomplete {
     // ...
     val isCancelling: Boolean get() = rootCause != null
     // ...
}

Cancelled(最终状态) : 对应的存储状态为 CompletedExceptionally,主要「记录异常信息」和 「当前异常是否被处理」

Kotlin 复制代码
internal open class CompletedExceptionally(
    @JvmField public val cause: Throwable,
    handled: Boolean = false
) {
    private val _handled = atomic(handled)
    val handled: Boolean get() = _handled.value
    fun makeHandled(): Boolean = _handled.compareAndSet(false, true)
}

Completed(最终状态) : 对应的存储状态为 Any ,可以是任意类型,主要取决于 complete 传入的内容

Tips: 上面除 最终状态 外,其余都是 Incomplete 的子类,主要用于 「判断当前 Job 的状态」和 「存放监听列表」

Kotlin 复制代码
internal interface Incomplete {
    val isActive: Boolean
    val list: NodeList? // 当为  Empty and JobNode 时候为空
}

状态转移图

Kotlin 复制代码
 New states      Active states       Inactive states

+---------+       +---------+                          }
| EMPTY_N | ----> | EMPTY_A | ----+                    } Empty states
+---------+       +---------+     |                    }
     |  |           |     ^       |    +----------+
     |  |           |     |       +--> |  FINAL_* |
     |  |           V     |       |    +----------+
     |  |         +---------+     |                    }
     |  |         | SINGLE  | ----+                    } JobNode states
     |  |         +---------+     |                    }
     |  |              |          |                    }
     |  |              V          |                    }
     |  |         +---------+     |                    }
     |  +-------> | SINGLE+ | ----+                    }
     |            +---------+     |                    }
     |                 |          |
     V                 V          |
+---------+       +---------+     |                    }
| LIST_N  | ----> | LIST_A  | ----+                    } [Inactive]NodeList states
+---------+       +---------+     |                    }
   |   |             |   |        |
   |   |    +--------+   |        |
   |   |    |            V        |
   |   |    |    +------------+   |   +------------+   }
   |   +-------> | COMPLETING | --+-- | CANCELLING |   } Finishing states
   |        |    +------------+       +------------+   }
   |        |         |                    ^
   |        |         |                    |
   +--------+---------+--------------------+

从创建协程看父子关系的建立

前置知识已经准备好了,从日常使用的代码 lifecycleScope.launch 查看父子关系的建立

Kotlin 复制代码
public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true) 
    coroutine.start(start, coroutine, block)
    return coroutine
}

private open class StandaloneCoroutine(
    parentContext: CoroutineContext,
    active: Boolean    // 当前值为 true
) : AbstractCoroutine<Unit>(parentContext, initParentJob = true, active = active)

public abstract class AbstractCoroutine<in T>(
    parentContext: CoroutineContext, // 父协程的上下文
    initParentJob: Boolean,          // 当前值为 true
    active: Boolean                  // 当前值为 true
) : JobSupport(active), Job, Continuation<T>, CoroutineScope {
    init {
        // initParentJob 为 true,执行 JobSupport#initParentJob
        if (initParentJob) initParentJob(parentContext[Job])
    }
}

public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob, SelectClause0 {

    // 当前 this 为 ChildJob
    protected fun initParentJob(parent: Job?) {
        /**
         * 如果当前没有父节点,就不用设置父节点的完成回调了
         * NonDisposableHandle 为空白的对象,什么都不做
         * 比如 GlobalScope.launch 的时候就没有 ParentJob
         */
        if (parent == null) {
            parentHandle = NonDisposableHandle
            return
        }
        
        /**
         * 确保 ParentJob 启动了
         * 如果 ParentJob 是 Lazy 模式,也会同步启动,毕竟 ChildJob 已经启动 ParentJob 怎么能不启动
         */
        parent.start()
        
        // 注意这里是 ParentJob 关联 ChildJob
        val handle = parent.attachChild(this)
        parentHandle = handle
        
        /**
         * 如果在 attachChild 如果已经完成的话,会直接更改 ChildJob 的状态,所以重置当前状态
         */
        if (isCompleted) {
            handle.dispose()    // 取消对 ParentJob 完成的监听
            parentHandle = NonDisposableHandle  // 重置 parentHandle 为空对象
        }
    }
    
    // 当前 this 为 ParentJob
    public final override fun attachChild(child: ChildJob): ChildHandle {
        // ChildHandleNode(child).asHandler => CompletionHandler 
        return invokeOnCompletion(onCancelling = true, handler = ChildHandleNode(child).asHandler) as ChildHandle
    }
    
    /**
     * 注意:当前 this 为 ParentJob
     * onCancelling:当状态处于 「Cancelling」 的时候也会回调
     * invokeImmediately: 当前已经处于对应可回调状态,则根据此值决定是否回调
     * 这2个变量主要控制在特殊情况下「更快的回调」和「减少插入」
     * 在 Incomplete 分支下都为 false 的时候,都是直接插入到 NodeList 中
     */
    public final override fun invokeOnCompletion(
        onCancelling: Boolean,       // true
        invokeImmediately: Boolean,  // true
        handler: CompletionHandler   // ChildHandleNode(child)
    ): DisposableHandle {
        /**
         * makeNode方法做了二件事
         * 1. 把 handler 包装 JobNode,但此处调用的 handler 为 ChildHandleNode,本身就是 JobNode,返回自身
         * 2. JobNode#job 赋值为 ParentJob
         */
        val node: JobNode = makeNode(handler, onCancelling)
        
        // 循环读取 state 当前值,把当前生成的 node 线程安全的插入进对应的状态中
        loopOnState { state ->
            when (state) {
                // 当前为没有监听的状态
                is Empty -> {
                    if (state.isActive) {
                        // 对应存储状态转移:「Empty_Active」 -> 「JobNode」
                        if (_state.compareAndSet(state, node)) return node
                    } else {
                        // 对应存储状态转移:「EMPTY_NEW」 -> 「InactiveNodeList」,这里只是推进状态并未插入,下次循环在插入
                        promoteEmptyToNodeList(state)
                    }
                }
                // 除 Empty 之外的未完成状态
                is Incomplete -> {
                    val list = state.list
                    if (list == null) { 
                        // 对应存储状态转移:「JobNode」 -> 「NodeList」,这里只是推进状态并未插入,下次循环在插入
                        promoteSingleToNodeList(state as JobNode)
                    } else {
                        /**
                         * 能走到此分支的剩余 InactiveNodeList、NodeList 和 Finishing 状态
                         * 因为前面已经 ParentJob#start, 所以这里只剩下 NodeList 和 Finishing 状态
                         */
                        var rootCause: Throwable? = null
                        var handle: DisposableHandle = NonDisposableHandle
                        
                        // 当前 onCancelling 为 true 且 ParentJob 是处于 Finishing 状态
                        if (onCancelling && state is Finishing) {
                            synchronized(state) {
                                rootCause = state.rootCause
                                /**
                                 * state.isCompleting = true 代表自身任务已经完成,在等待 ChildJob
                                 * 如果当前已经有异常且自身Job完成,则在下面直接回调,不需要加入链表
                                 * 而当前 Job 还没完成,则有概率出现其他异常,需要加入链表等待分发
                                 */
                                if (rootCause == null || handler.isHandlerOf<ChildHandleNode>() && !state.isCompleting) {
                                    // 如果加入 list 失败则重试
                                    if (!addLastAtomic(state, list, node)) return@loopOnState
                                    if (rootCause == null) return node  // Completing 直接返回
                                    handle = node
                                }
                            }
                        }
                        if (rootCause != null) {
                            // 这里开始手动分发事件
                            if (invokeImmediately) handler.invokeIt(rootCause)
                            return handle
                        } else {
                            if (addLastAtomic(state, list, node)) return node
                        }
                    }
                }
                // 此分支是完成的状态
                else -> { 
                    /**
                     * 当前 ParentJob 已经完成,同时 invokeImmediately 为 true
                     * 直接调用 handler 回调给 ChildJob 告诉 ParentJob 已经完成,同时带上是异常结束还是正常结束的
                     */
                    if (invokeImmediately) handler.invokeIt((state as? CompletedExceptionally)?.cause)
                    // 返回一个空的 Handle
                    return NonDisposableHandle
                }
            }
        }
    }
}

JobSupport#attachChild 在此方法中,Job#invokeOnCompletion 会有二种返回值

  1. NonDisposableHandle ,实现 DisposableHandleChildHandle
  2. ChildHandleNode ,实现 JobCancellingNodeChildHandle 所以这里强转 ChildHandle 可正常运行

但对于一般 Job#invokeOnCompletion 调用来说,返回值取决于传入的 handler, 是另外二种返回值

  1. NonDisposableHandle ,实现 DisposableHandleChildHandle
  2. JobNode 及其子类,实现 CompletionHandlerBaseDisposableHandleIncomplete

总结:除 JobSupport#attachChild 外,其余都为 DisposableHandle 子类「主要用于 取消监听

协程的取消

因为 JobCompletableJobCoroutine Job 两种, 所以 Job#cancel 也对应有2种;

在整个「取消 」过程除 cancel 入口外,分为两个步骤

  1. 第一步骤:Job#tryMakeCompletingJob#tryMakeCompletingSlowPath, 从 「InComplete 」 推进到 「Finishing 」 状态,主要等待 自身blockChildJob 完成;同时正常结束也是相同步骤,只是 proposedUpdate 参数不为 CompletedExceptionally
  2. 第二步骤:Job#finalizeFinishingState 从 「Finishing 」 推进到 「终止状态

cancel 入口

Kotlin 复制代码
public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob, SelectClause0 {

    // ============================= cancel 的入口 =============================
    public override fun cancel(cause: CancellationException?) {
        cancelInternal(cause ?: defaultCancellationException())
    }
    
    public open fun cancelInternal(cause: Throwable) {
        cancelImpl(cause)
    }
    
    // 返回值代表消费了此异常
    internal fun cancelImpl(cause: Any?): Boolean {
        var finalState: Any? = COMPLETING_ALREADY
        
        /**
         * onCancelComplete 为 true 则代表是 CompletableJob
         */
        if (onCancelComplete) {
            // 开始取消 Job 然后返回取消后的状态
            finalState = cancelMakeCompleting(cause)
            // 如果当前等待 ChildJob 完成,则什么都不做
            if (finalState === COMPLETING_WAITING_CHILDREN) return true
        }
        
        /**
         * 主要是处理 Coroutine Job 的情况
         */
        if (finalState === COMPLETING_ALREADY) {
            finalState = makeCancelling(cause)
        }
        
        return when {
            finalState === COMPLETING_ALREADY -> true            // 代表已经 Completing 过了
            finalState === COMPLETING_WAITING_CHILDREN -> true   // 等待 ChildJob 完成中
            finalState === TOO_LATE_TO_CANCEL -> false           // 已经完成,取消的太晚
            else -> {
                afterCompletion(finalState)
                true
            }
        }
    }
    
    // ============================= CompletableJob 的 cancel =============================
    /**
     * CompletableJob 的 cancel,返回值包括三种
     * 1. COMPLETING_ALREADY:代表已经 Completing 过了
     * 2. COMPLETING_WAITING_CHILDREN: 等待 ChildJob 完成中
     * 3. final state: 已经为最终状态
     * tryMakeCompleting 方法 CAS 成功后状态至少为 「Finishing」
     */
    private fun cancelMakeCompleting(cause: Any?): Any? {
        // 循环获取 state 状态
        loopOnState { state ->
            // 如果当前已经完成 || 「已经是 Finishing 状态 且 Job block 已完成」
            if (state !is Incomplete || state is Finishing && state.isCompleting) {
                return COMPLETING_ALREADY   // 代表已经 Completing 过了
            }
            
            // 「Cancelled」状态载体,其中内部会调用 ParentJob#getChildJobCancellationCause 方法
            val proposedUpdate = CompletedExceptionally(createCauseException(cause))
            
            // 尝试把当前「状态」推进到 「Cancelled」状态
            val finalState = tryMakeCompleting(state, proposedUpdate)
            
            // 推进成功返回状态
            if (finalState !== COMPLETING_RETRY) return finalState
        }
    }
    
    
    // ============================= Coroutine Job 的 cancel =============================
    /**
     * 虽然从上述代码流程会 CompletableJob 也会走进来
     * 但是主要是处理 Coroutine Job 的取消
     * 原因 CompletableJob 在 cancelMakeCompleting 方法中,状态至少为 「Finishing」甚至有可能为 「最终状态」,则
     * 如果以 「Cancelling」状态进来,基本是跳过执行的
     * 如果以 「Completing」 进来则变成 「Cancelling」 
     *
     * Coroutine Job 和 CompletableJob 主要的区别在于 block 的执行,所以代码中根据 isActive 做处理
     * 如果 isActive 为 true,则 block 正在执行,只需要变更状态等 block 执行完成后调用 complete 函数(下面会详细说明)
     * 如果 isActive 为 false,就跟 CompletableJob 是一致的,直接调用 tryMakeCompleting 变更状态 
     *
     * Coroutine Job 的 cancel,返回值包括四种
     * 1. COMPLETING_ALREADY
     * 2. TOO_LATE_TO_CANCEL
     * 3. COMPLETING_WAITING_CHILDREN
     * 4. finalState
     */ 
    private fun makeCancelling(cause: Any?): Any? {
        var causeExceptionCache: Throwable? = null 
        // 循环
        loopOnState { state ->
            when (state) {
                is Finishing -> { 
                    val notifyRootCause = synchronized(state) {
                        // Finishing 已经密封了不允许在添加异常了
                        if (state.isSealed) return TOO_LATE_TO_CANCEL
                        
                        val wasCancelling = state.isCancelling
                        if (cause != null || !wasCancelling) {
                            val causeException = causeExceptionCache ?: createCauseException(cause).also { causeExceptionCache = it }
                            state.addExceptionLocked(causeException)
                        }
                        // 原先没有分发则返回 true 进行事件分发
                        state.rootCause.takeIf { !wasCancelling }
                    }
                    notifyRootCause?.let { notifyCancelling(state.list, it) }
                    return COMPLETING_ALREADY 
                }
                // Finishing 之外的 Incomplete
                is Incomplete -> {
                    // 得到异常
                    val causeException = causeExceptionCache ?: createCauseException(cause).also { causeExceptionCache = it }
                    
                    if (state.isActive) {
                        // 转换为 Cancelling 等待 block 完成
                        if (tryMakeCancelling(state, causeException)) return COMPLETING_ALREADY
                    } else {
                        // block 未运行直接使用 tryMakeCompleting 过渡
                        val finalState = tryMakeCompleting(state, CompletedExceptionally(causeException))
                        when {
                            finalState === COMPLETING_ALREADY -> error("Cannot happen in $state")
                            finalState === COMPLETING_RETRY -> return@loopOnState
                            else -> return finalState
                        }
                    }
                }
                // 已经完成则返回
                else -> return TOO_LATE_TO_CANCEL
            }
        }
    }
   
}

Job#tryMakeCancelling 只需变更状态为 Cancelling 等待 block 执行完成后会自动下去,这里可能有人看不懂,而我们知道 CorountineScope.launch 的时候会得到 StandaloneCoroutine

Kotlin 复制代码
private open class StandaloneCoroutine(
    parentContext: CoroutineContext,
    active: Boolean
) : AbstractCoroutine<Unit>(parentContext, initParentJob = true, active = active) 

public abstract class AbstractCoroutine<in T>(
    parentContext: CoroutineContext,
    initParentJob: Boolean,
    active: Boolean
) : JobSupport(active), Job, Continuation<T>, CoroutineScope {

    public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
        // 这里会调用 CoroutineStart.invoke 方法
        start(block, receiver, this)
    }
            
    /**
     * start(block, receiver, this) 等价于 block.startCoroutineCancellable(receiver, completion) 
     * 而在 block 执行完成的时候就会调用 Continuation#resumeWith 方法,即当前方法
     * 而具体为什么执行,这是 Kotlin Coroutine 相关知识了,有空再出一遍文章说明 
     *
     * 可以看到调用了 makeCompletingOnce 函数,而 makeCompletingOnce 只有两个返回值
     * 1. COMPLETING_WAITING_CHILDREN
     * 2. 一个是最终状态
     */ 
    public final override fun resumeWith(result: Result<T>) {
        val state = makeCompletingOnce(result.toState())
        if (state === COMPLETING_WAITING_CHILDREN) return
        afterResume(state)
    }
   
    internal fun makeCompletingOnce(proposedUpdate: Any?): Any? {
        loopOnState { state ->
            /**
             *  当前 block 完成,调用 tryMakeCompleting 过渡到最终状态了
             */
            val finalState = tryMakeCompleting(state, proposedUpdate)
            when {
                finalState === COMPLETING_ALREADY ->
                    throw IllegalStateException(
                        "Job $this is already complete or completing, " +
                            "but is being completed with $proposedUpdate", proposedUpdate.exceptionOrNull
                    )
                finalState === COMPLETING_RETRY -> return@loopOnState
                else -> return finalState // COMPLETING_WAITING_CHILDREN or final state
            }
        }
    }
    
}

第一步骤

Incomplete 」 和 proposedUpdate 过渡 「Finishing」状态

Kotlin 复制代码
public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob, SelectClause0 {
    
    private fun tryMakeCompleting(state: Any?, proposedUpdate: Any?): Any? {
        // 已经处于完成状态直接返回 COMPLETING_ALREADY
        if (state !is Incomplete) return COMPLETING_ALREADY
        
         /**
          * 如果当前只有「0 或 1」个监听 && 不是 ChildJob 的监听 && 推进的不是「Cancelled」 状态
          * 则可以快速转换为最终状态,原因
          * 1. 不是 ChildJob 监听,可以不用等待 ChildJob 的完成
          * 2. 「Cancelled」因为存在多线程,需要锁上分发才保证不会丢失,不适合快速转换
          */
        if ((state is Empty || state is JobNode) && state !is ChildHandleNode && proposedUpdate !is CompletedExceptionally) {
            // CAS 转换成功则直接返回最终状态
            if (tryFinalizeSimpleState(state, proposedUpdate)) return proposedUpdate
            // CAS 转换失败,重试
            return COMPLETING_RETRY
        }
        
        return tryMakeCompletingSlowPath(state, proposedUpdate)
    }
    
    private fun tryMakeCompletingSlowPath(state: Incomplete, proposedUpdate: Any?): Any? {
        // 从 「Incomplte」 获取 「NodeList」
        val list = getOrPromoteCancellingList(state) ?: return COMPLETING_RETRY
        // 「Finishing」 也属于 「Incomplete」,所以需要判断一下
        val finishing = state as? Finishing ?: Finishing(list, false, null)
        
        var notifyRootCause: Throwable? = null
        synchronized(finishing) {
            if (finishing.isCompleting) return COMPLETING_ALREADY
            // 进入此方法代表 Job#block 已经执行完成
            finishing.isCompleting = true
            if (finishing !== state) {
                // CAS 切换到 Finishing
                if (!_state.compareAndSet(state, finishing)) return COMPLETING_RETRY
            }
           
            assert { !finishing.isSealed } 
            // 记录原先 isCancelling 状态
            val wasCancelling = finishing.isCancelling
            // 如果是推进到 「Cancelled」状态,则把当前异常添加进去
            (proposedUpdate as? CompletedExceptionally)?.let { finishing.addExceptionLocked(it.cause) }
            // finishing.rootCause != null && 之前不是取消异常 => 分发异常
            notifyRootCause = finishing.rootCause.takeIf { !wasCancelling }
        }
        
       /**
        * notifyCancelling 主要干了3件活
        * 1. 自身取消
        * 2. 仅通知完成回调的相关 JobCancellingNode 节点(invokeOnCompletion onCancelling 为 true),
        *    而所有的 ChildJob 都是 JobCancellingNode 子类,所有的 ChildJob 都会接收到
        * 3. 取消 ParentJob
        */
        notifyRootCause?.let { notifyCancelling(list, it) }
        
        // 查找 ChildHandleNode(即ChildJob),ChildJob 完成后会自动移除,在此列表都是为完成的
        val child = firstChild(state)
        
        // ChildJob 存在,则尝试等待 ChildJob 完成,返回等待 Child 状态
        if (child != null && tryWaitForChild(finishing, child, proposedUpdate))
            return COMPLETING_WAITING_CHILDREN
            
        // 从 Finishing 推进到指定状态 「Cancelled」 或者 「Completed」状态
        return finalizeFinishingState(finishing, proposedUpdate)
    }
    
    /**
     * tailrec 是 Kotlin 的关键字,当自身函数是调用自身的话,会优化递归调用逻辑
     * 返回值代表是否在等待 ChildJob 
     */
    private tailrec fun tryWaitForChild(state: Finishing, child: ChildHandleNode, proposedUpdate: Any?): Boolean {
        // 对当前 ChildJob 进行完成监听,在完成的时候会调用下方 continueCompleting 方法
        val handle = child.childJob.invokeOnCompletion(
            invokeImmediately = false,   // 已完成不需要直接执行
            handler = ChildCompletion(this, state, child, proposedUpdate).asHandler
        )
        // 由建立父子关系可知,invokeOnCompletion 不为 NonDisposableHandle 代表监听 ChildJob 成功
        if (handle !== NonDisposableHandle) return true
        
        // 当前 ChildJob 已经完成查找下一个
        val nextChild = child.nextChild() ?: return false
        return tryWaitForChild(state, nextChild, proposedUpdate)
    }
    
    // 监听 ChildJob 任务完成后会调用此方法(由 ChildCompletion 调用)
    private fun continueCompleting(state: Finishing, lastChild: ChildHandleNode, proposedUpdate: Any?) {
        // 一致性检查
        assert { this.state === state }
        
        // 继续查找是否有 ChildJob 要等待
        val waitChild = lastChild.nextChild()
        if (waitChild != null && tryWaitForChild(state, waitChild, proposedUpdate)) return 
        
        // 没有任何 ChildJob 则调用 finalizeFinishingState 推进到最终状态
        val finalState = finalizeFinishingState(state, proposedUpdate)
        // 通知当前 Job 已经完成,一般用于恢复调用 withContext 或者 coroutineScope 所在的协程
        afterCompletion(finalState)
    }
}

第二步骤

从 「Finishing 」推进 proposedUpdateCompleted 」或 「Cancelled」状态

Kotlin 复制代码
public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob, SelectClause0 {
    private fun finalizeFinishingState(state: Finishing, proposedUpdate: Any?): Any? {
        // 一致性检查
        assert { this.state === state } 
        assert { !state.isSealed }
        assert { state.isCompleting }
        
        val proposedException = (proposedUpdate as? CompletedExceptionally)?.cause
        var wasCancelling = false
        // 得出最后异常
        val finalException = synchronized(state) {
            wasCancelling = state.isCancelling
            val exceptions = state.sealLocked(proposedException)
            val finalCause = getFinalRootCause(state, exceptions)
            if (finalCause != null) addSuppressedExceptions(finalCause, exceptions)
            finalCause
        }
        
        /**
         * 根据条件计算最终转移状态
         * 1. finalException 没有任何异常,则使用 proposedUpdate 的状态「any」
         * 2. finalException 等于当前推进的异常,则使用 proposedUpdate
         * 3. 有其他异常,则使用 CompletedExceptionally 包裹返回
         */
        val finalState = when {
            finalException == null -> proposedUpdate
            finalException === proposedException -> proposedUpdate
            else -> CompletedExceptionally(finalException)
        }
        
        // 处理异常[下文会交接]
        if (finalException != null) {
            val handled = cancelParent(finalException) || handleJobException(finalException)
            if (handled) (finalState as CompletedExceptionally).makeHandled()
        }
        
        // 分发回调 
        if (!wasCancelling) onCancelling(finalException)
        onCompletionInternal(finalState)
        
        // 「Finishing」 推进到 「完成状态」
        val casSuccess = _state.compareAndSet(state, finalState.boxIncomplete())
        assert { casSuccess }
        
        /**
         * 收尾工作
         * 1. 清理对 ParentJob 的监听
         * 2. 分发监听当前 ParentJob 的完成事件,注意这里是 JobNode,所有的监听都是 JobNode 的子类,
         *    所以分发给所有节点
         */
        completeStateFinalization(state, finalState)
        // 返回最后状态
        return finalState
    }
}

协程的等待

其实 Job#join 的挂起函数本质也是等待 Job 的完成,所以源码也是调用 Job#invokeOnCompletion 设置 Job 的完成回调,在收到回调之后恢复 Job#join 挂起函数的运行

Kotlin 复制代码
public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob, SelectClause0 {
    public final override suspend fun join() {
        // 检测 Job 环境是否为运行状态,如果不是则直接抛出 CancellationException 异常,不用挂起等待
        if (!joinInternal()) {
            /**
             * 注意:这里 coroutineContext 为 suspend 函数上下文,而不是 JobSupport 的上下文
             * 用于检查当前执行 suspend 函数的状态,如果已经是非 isActive 状态则抛出 CancelException
             */
            coroutineContext.ensureActive()
            return
        }
        // 当前在运行状态,则挂起等待完成
        return joinSuspend()
    }

    private fun joinInternal(): Boolean {
        // 循环读取状态
        loopOnState { state ->
            // 如果 state 不是 Incomplete 子类且「非Incomplete」已经是最终状态,无后继性,可以直接返回 false
            if (state !is Incomplete) return false 
            // 判断是否处于已启动状态
            if (startInternal(state) >= 0) return true
        }
    }
    
    // 下方函数解析
    private fun startInternal(state: Any?): Int {
        when (state) {
            is Empty -> { 
                if (state.isActive) return FALSE
                // 对应「EMPTY_NEW」 -> 「EMPTY_ACTIVE」 状态转移
                if (!_state.compareAndSet(state, EMPTY_ACTIVE)) return RETRY 
                onStartInternal()
                return TRUE
            }
            is InactiveNodeList -> {
                // 对应 「InactiveNodeList」 -> 「NodeList」状态转移
                if (!_state.compareAndSet(state, state.list)) return RETRY  // -1
                onStartInternal()
                return TRUE 
            }
            else -> return FALSE
        }
    }
    
    // 下方函数解析
    private suspend fun joinSuspend() = suspendCancellableCoroutine<Unit> { cont ->
        cont.disposeOnCancellation(invokeOnCompletion(handler = ResumeOnCompletion(cont).asHandler))
    }
}

JobSupport#startInternal 返回值有3个

  1. RETRY 为 -1:代表 CAS 修改变量失败,需要重试
  2. FALSE 为 0:代表当前 Job 早已经启动
  3. TRUE 为 1: 代表 Job 启动成功

这里主要判断 Job 是否已经启动;注意 JobSupport#startInternal高并发场景 下可能是以 最终状态 进入的,最后就会进入 JobSupport#joinSuspend,但在 JobSupport#invokeOnCompletion 的时候会直接 调用完成回调 ;如上面 ChildJob 加入已完成的 ParentJob 直接取消是一致的

JobSupport#joinSuspend 主要流程解析:

  1. ResumeOnCompletion 从下面可知,本质是在 Job 完成的时候恢复 cont 执行

    Kotlin 复制代码
    private class ResumeOnCompletion(private val continuation: Continuation<Unit>) : JobNode() {
        override fun invoke(cause: Throwable?) = continuation.resume(Unit)
    }
  2. invokeOnCompletion 会返回一个 DisposableHandle ,然后传入 cont.disposeOnCancellation 作为参数,这里的 DisposableHandle 主要是取消对 Job 的监听回调

  3. 使用 DisposeOnCancel 包装 DisposableHandle 然后作为参数传入 invokeOnCancellation;本质的意思是在 suspend 函数 cancel 的时候,能取消对 Job 的监听回调,这里的源码与 Job 基本是一致的

    Kotlin 复制代码
    public fun CancellableContinuation<*>.disposeOnCancellation(handle: DisposableHandle): Unit =
        invokeOnCancellation(handler = DisposeOnCancel(handle).asHandler)
    
    private class DisposeOnCancel(private val handle: DisposableHandle) : CancelHandler() {
        override fun invoke(cause: Throwable?) = handle.dispose()
    }

协程异常捕获

当我们想捕获协程中的异常,经常在 launch 中 使用 CoroutineExceptionHandler 捕获,近似下面代码,此时会正常输出 catch throwable

Kotlin 复制代码
fun main() {
    val scope = CoroutineScope(SupervisorJob())
    val job = scope.launch(CoroutineExceptionHandler { _, _ -> println("catch throwable") }) {
        error("")
    }

    runBlocking {
        job.join()
    }
}

而当我们如果修改一下,把捕获改到内层,此时发现应用是崩溃的

Kotlin 复制代码
fun main() {
    val scope = CoroutineScope(SupervisorJob())
    val job = scope.launch {
        launch(CoroutineExceptionHandler { _, _ -> println("catch throwable")}) {
            error("")
        }
    }

    runBlocking {
        job.join()
    }
}

此时结合源码来分析下 异常分发 流程

Kotlin 复制代码
public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob, SelectClause0 {
    // 从 「Finishing」 过渡到 「终止状态」
    private fun finalizeFinishingState(state: Finishing, proposedUpdate: Any?): Any? {
        // ...
        if (finalException != null) {
            val handled = cancelParent(finalException) || handleJobException(finalException)
            if (handled) (finalState as CompletedExceptionally).makeHandled()
        }
        // ...
        return finalState
    }
    
    // 当前为 ChildJob
    private fun cancelParent(cause: Throwable): Boolean {
        /**
         * 如果当前为 coroutineScope 或者 withContext 等,则直接返回
         * 原由这些是 suspend函数 转换 CoroutineScope 环境的
         * 内部异常应由 suspend 所抛出,而不是走 Job 的事件分发
         */
        if (isScopedCoroutine) return true

        val isCancellation = cause is CancellationException
        val parent = parentHandle
        
        // 当前没有 ParentJob,则根据是否 CancellationException 返回对应异常是否被处理
        if (parent === null || parent === NonDisposableHandle) {
            return isCancellation
        }

        // 往父类分发 - 同时 CancellationException 永远返回 true 代表消耗
        return parent.childCancelled(cause) || isCancellation
    }
    
    // 当前为 ParentJob
    public open fun childCancelled(cause: Throwable): Boolean {
        // 如果从子类过来的异常是 CancellationException 则直接返回,不取消
        if (cause is CancellationException) return true
        
        return cancelImpl(cause) && handlesException
    }
}

Job#finalizeFinishingState -> ChildJob#cancelParent -> ParentJob#childCancelled 来看,只能看到异常分发流程,在回过去看 StandaloneCoroutine#handleJobException 对应的实现

Kotlin 复制代码
private open class StandaloneCoroutine(
    parentContext: CoroutineContext,
    active: Boolean
) : AbstractCoroutine<Unit>(parentContext, active) {
    override fun handleJobException(exception: Throwable): Boolean {
        handleCoroutineException(context, exception)
        return true
    }
}

public fun handleCoroutineException(context: CoroutineContext, exception: Throwable) {
    try {
        context[CoroutineExceptionHandler]?.let {
            it.handleException(context, exception)
            return
        }
    } catch (t: Throwable) {
        handleCoroutineExceptionImpl(context, handlerException(exception, t))
        return
    }
    handleCoroutineExceptionImpl(context, exception)
}

context[CoroutineExceptionHandler] 可知开始使用外部注入的 CoroutineExceptionHandler , 可为什么还会异常呢;因为异常分发是先往 ParentJob 走,直到没有 ParentJob 为止或像 SupervisorJob 一样中断异常的分发,然后使用当前 Job 调用 handleCoroutineException 进行异常处理,所以只有最外层 ParentJobSupervisorJob 才能使用 CoroutineExceptionHandler 处理异常

Kotlin 复制代码
public fun SupervisorJob(parent: Job? = null) : CompletableJob = SupervisorJobImpl(parent)

private class SupervisorJobImpl(parent: Job?) : JobImpl(parent) {
    // 中断往上的分发
    override fun childCancelled(cause: Throwable): Boolean = false
}

刚刚我们说了 isScopedCoroutine 异常是由 suspend 函数抛出,我们来看下 coroutineScope 源码

Kotlin 复制代码
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return suspendCoroutineUninterceptedOrReturn { uCont ->
        val coroutine = ScopeCoroutine(uCont.context, uCont)
        coroutine.startUndispatchedOrReturn(coroutine, block)
    }
}

internal open class ScopeCoroutine<in T>(
    context: CoroutineContext,
    @JvmField val uCont: Continuation<T> // unintercepted continuation
) : AbstractCoroutine<T>(context, true, true), CoroutineStackFrame {

    override fun afterCompletion(state: Any?) {
        // 恢复 suspend 函数运行
        uCont.intercepted().resumeCancellableWith(recoverResult(state, uCont))
    }

    override fun afterResume(state: Any?) {
        // 恢复 suspend 函数运行
        uCont.resumeWith(recoverResult(state, uCont))
    }
}

internal fun <T> recoverResult(state: Any?, uCont: Continuation<T>): Result<T> =
    if (state is CompletedExceptionally) 
        Result.failure(recoverStackTrace(state.cause, uCont))   // 「Cancelled」返回异常
    else
        Result.success(state as T)    // 「Completed」返回结果值

上述的 ScopeCoroutine 中, afterCompletionafterResume 的区别

afterCompletionafterResume 其实都是 Job#blockChildJob 同时完成才调用,区别在于 afterCompletion 是 「after ChildJob Completion」即 ChildJob 后完成,而 afterResume 是「after Continuation resume」即 job#block 后完成;

对应 coroutineScope 来说

  1. afterCompletion 代表 ChildJob 后完成,有可能改变了 CoroutineContext ,所以需要 intercepted 切回原先的 CoroutineContextresume
  2. afterResume 代表 Job#block 后完成,coroutineScope 不会修改 CoroutineContext ,所以可以直接 resumeKotlinGoogle 一样,在性能方面做到 极致
相关推荐
烬奇小云3 小时前
认识一下Unicorn
android·python·安全·系统安全
顾北川_野15 小时前
Android 进入浏览器下载应用,下载的是bin文件无法安装,应为apk文件
android
CYRUS STUDIO15 小时前
Android 下内联汇编,Android Studio 汇编开发
android·汇编·arm开发·android studio·arm
右手吉他15 小时前
Android ANR分析总结
android
PenguinLetsGo17 小时前
关于 Android15 GKI2407R40 导致梆梆加固软件崩溃
android·linux
杨武博19 小时前
音频格式转换
android·音视频
音视频牛哥21 小时前
Android音视频直播低延迟探究之:WLAN低延迟模式
android·音视频·实时音视频·大牛直播sdk·rtsp播放器·rtmp播放器·android rtmp
ChangYan.1 天前
CondaError: Run ‘conda init‘ before ‘conda activate‘解决办法
android·conda
二流小码农1 天前
鸿蒙开发:ForEach中为什么键值生成函数很重要
android·ios·harmonyos
夏非夏1 天前
Android 生成并加载PDF文件
android