第一步

launch 过程分析

第二步

start() 的第二、三个参数是同一个,都是第一张图中构建的 StandaloneCoroutine 目标

launch 过程分析

第三步

第二步中 CoroutineStart 完结了 invoke() 办法,所以能够直接当办法调用。

launch 过程分析

第四步

launch 过程分析

第五步

回来 lambda 表达式对应的目标。

能够看到办法名前有 actual 润饰,表明它是在一个详细的平台中的完结

launch 过程分析

create() 办法界说在 BaseContinuationImpl 中,但该办法未完结,详细的完结在 lambda 对应的类中。比如在 MainActivity 中如下书写,对应的类名便是 MainActivity$test$1:

private fun test(){
    mScope.launch {
        Log.e(TAG, "test:" )
    }
}

经过 Javap 反编译该 class 文件,检查对应的 create() 办法,字节码如下,从指令能够看出该办法便是回来 launch() 中 lambda 表达式对应的类的目标。

launch 过程分析

从字节码还能够看出尽管传入了两个参数,但只运用了第一个。而且经过结构函数的能够发现,它只是将参数传入给父类。结构函数对应的指令如下:

launch 过程分析

第五步半

从第五步能够看出第四步中的第一个办法 createCoroutineUnintercepted() 回来的便是 lambda 对应的类目标,该类的父类是 SuspendLambda,但它并没有额定逻辑。

launch 过程分析

现在到 SuspendLambda 的父类 ContinuationImpl 的结构函数

launch 过程分析

ContinuationImpl 会调用父类 BaseContinuationImpl 结构函数,父类会运用 completion 记录下传入的参数,也便是第一步创立的 StandaloneCoroutine 目标。

到此第四步中第一个办法算是剖析完结,总结一下便是回来 lambda 对应的类目标,一起运用 completion 引证第一步创立的 StandaloneCoroutine 目标

第六步

书接第四步,第五步及第五步半剖析完了第一个办法,下面就该调用 intercepted() 办法。该办法详细完结如上图。第一次调用时 intercepted() 必定为 null,所以履行到

// 留意这个 this,它便是 lambda 表达式对应的类目标
context[ContinuationInterceptor]?.interceptContinuation(this)

前面回来的是 Dispatchers 目标,interceptContinuation() 完结在 CoroutineDispatcher 中(一切 Dispatchers 的父类)

launch 过程分析

结构函数如下:

launch 过程分析

所以第六步其实便是回来一个 DispatchedContinuation 目标。

现在总结一下:DispatchedContinuationcontinuation 特点指向 lambda 目标,lambda 目标运用 completion 指向 launch() 中创立的 StandaloneCoroutin 目标

第七步

书接第四步。从第五步到第六步都履行完后,就到了第四步中的 resumeCancellableWith()。if 判别只要在 Dispatchers.Unconfined 时才不成立,疏忽。

launch 过程分析

第八步

线程切换核心点

第七步的 dispatcher#dispatch() 终究会到 CoroutineSchedulerdispatch(),整个进程比较简单,疏忽。下面都以 Dispatchers.Default 为例进行阐明。

launch 过程分析

从上图能够看出该步首要涉及到线程切换,此处不细剖析。但无论线程如何切换,终究必定履行 DispatchedContinuationrun() 办法

第九步

DispatchedContinuation 自身并没有重写 run() 办法,详细的完结都在其父类 DispatchedTask

launch 过程分析

这一步完毕后 DispatchedContinuation 的功能就完结了:它首要用来进行线程切换,lambda 目标的 resume() 便是运转在切换后的线程中。

第十步

第九步到 Continuationresume(),它会调用 resumeWith()

public inline fun <T> Continuation<T>.resume(value: T): Unit =
    resumeWith(Result.success(value))

resumeWith() 界说在 BaseContinuationImpl 中,它是 lambda 目标的先人类。上面说过 lambda 目标经过 completion 特点引证第一步创立的 StandaloneCoroutine 目标,completion 便是界说在 BaseContinuationImpl 类中

launch 过程分析

*invokeSuspend()* 逻辑比较简单,它内部便是状态机。

从逻辑上讲 completion 是当时协程的父协程(不一定是直接父协程),所以当时协程履行完结后需求再次循环调用父协程的 invokeSuspend(),这样父协程才能在挂起点康复履行,并且能拿到子协程的回来值。

completion#resumeWith() 也是进行康复操作,它有可能会康复外层协程。总归,经过 resumeWith() 协程会从内到外依次被康复

第十一步

第十步中最终履行到 completion.resumeWith()。该办法是 final 类型的,所以无论哪种 coroutine 逻辑都相同。该办法界说在 AbstractCoroutine 类中,它是 StandaloneCoroutine 的父类

launch 过程分析

makeCompletingOnce() 便是回来参数,疏忽。

假如子类未重写任何办法,afterResume() 会调用 afterCompletion()。后者办法界说在 JobSupport 中,是个空完结。至此整个协程履行完毕。这也是经过 launch() 启动一个协程的一切流程。

假如协程中调用了挂起函数,那么 completion.resumeWith() 就需求担任康复它的外层协程。此处以 withContext() 为例阐明

第十一步半

假如运用 withContext,lambda 表达式对应的目标依旧是 BaseContinuationImpl 类型,依旧运用 completion 引证外层。运用 launch() 时外层是 StandaloneCoroutine 类型,运用 withContext() 时则是 DispatchedCoroutine类型(详细进程不剖析),该类重写了 afterResume(),该办法的入参便是 withContext() 的回来值。

launch 过程分析

最终一行,它的履行逻辑与第四步开始时相同,有几点不同:

  1. 第七步 resumeCancellableWith 的第一个参数是 withContext() 的回来成果的包装。一起,第七步会运用 _state 记录下成果(这是 withContext() 的真实回来成果)

  2. 第九步,运用 takeState() 取上第七步保存的 _state,并把该成果经过 resume() 办法会给了 withContext() 的外层协程

  3. 第十步,invokeSuspend() 就收到了内层协程的回来值

线程切换简说

从上面十几步能够发现:

  1. lambda 表达式会被转换成一个目标(称为 Continuation 目标),该目标在内部会被包装成 DispatchedContinuation 目标(即第四步中 intercepted() 的效果)。后者会运用 Dispatchers 进行线程切换。一旦线程切换完结 DispatchedContinuation 目标的效果就完毕了,它会在新线程中履行 Continuation 目标的 invokeSuspend() 办法,也便是我们书写的 lambda 表达式

  2. 在 lambda 表达式中遇到新协程时,会按上面的流程结构了下新的目标进行线程切换并履行。一起内层 Continuation 目标会持有外层 Continuation 目标的引证,方便康复外层协程的履行

  3. 假如是挂起函数,在履行完结时会将外层 Continuation 目标从头封装成 DispatchedContinuation 目标,然后再履行。这首要是为了让外层协程能够运转在挂起前的线程中。

总结

整个 launch 进程剖析完结,首要留传有线程切换逻辑,后面再说。

  1. 子协程间接持有父协程引证。所以子协程履行完结后,能够经过 resumeWith() 办法唤醒父协程
    • 这儿的唤醒其实不太精确。更精确说应该是子线程经过 resumeWith() 从头调用父协程的 invokeSuspend(),该办法内部会运用状态机形式记录父协程运转到的位置,被从头调用后会接着往下履行。
    • 因为 resumeWith() 能够接收参数,所以子协程履行完结后的成果能够传递到父协程的 invokeSuspend() 中
  2. 所谓协程,在代码层面上便是一个 Continuation 目标