协程启动到执行

254 阅读1分钟

协程创建

demo

suspend {
    Log.d(TAG, "suspend block:")
    "123"
}.createCoroutine(object : Continuation<String> {
    override val context: CoroutineContext
        get() = EmptyCoroutineContext
    override fun resumeWith(result: Result<String>) {
        val value = result.getOrNull()
        Log.d(TAG, "resumeWith:$value")
    }
}).resume(Unit)
Log.d(TAG, "onCreate")

上面日志先打印suspend中的代码块,然后执行Continuation的resumeWith,最后执行主线程的代码。

createCoroutine

image.png

是挂起函数的扩展方法,方法参数是Continuation类型,对应上面的匿名内部类。创建了一个SafeContinuation对象,它也是一个Continuation类型,并传递两个参数。

resume

image.png

resume方法会调用resumeWith,看下SafeContinuation的resumeWith方法: image.png

此处提醒下,kotlin的源码需要到对应的** Jvm类下找,要不然方法只是一个申明。此处的result是构造SafeContinuation传递进来的COROUTINE_SUSPENDED,因此会执行delegate.resumeWith(result),此处的delegate是createCoroutineUnintercepted(completion).intercepted()创建的。

createCoroutineUnintercepted

它是挂起函数的扩展方法:

image.png 判断当前挂起函数是不是BaseContinuationImpl类型,如果是则调用create方法。

此时可以打开字节码,看下上面的(suspend () -> T)是什么对象?

image.png 可以看到createCoroutine方法传入了两个参数,我们都知道扩展函数最终编译出来的方法第一个参数是被扩展对象,所以此处的CoroutineActivityonCreateonCreate1就是(suspend () -> T),CoroutineActivityonCreateonCreate2对应的是例子中的Continuation匿名内部类。我们注意下,此时传入CoroutineActivityonCreateonCreate1中的Continuation参数是null。

image.png

image.png 而SuspendLambda的继承关系如下:

image.png 所以会调用挂起函数的create方法:

image.png 父类中要求子类必须重写该方法,我们看CoroutineActivityonCreateonCreate1的create方法:

image.png 此时重新new了一个CoroutineActivityonCreateonCreate1,并把completion传入其中,而此处的completion就是上面的CoroutineActivityonCreateonCreate2,它是一个Continuation。而开端在分析createCoroutine的时候,创建CoroutineActivityonCreateonCreate1传入的Continuation是null。

不太明白,为什么不在createCoroutine时候直接直接把CoroutineActivityonCreateonCreate2传入到CoroutineActivityonCreateonCreate1中,而非要通过create方法再创建一个CoroutineActivityonCreateonCreate1。

我们再来看intercepted方法。

intercepted

image.png

是Continueation的扩展方法,当然了,刚刚create创建的CoroutineActivityonCreateonCreate1是一个suspendLambda对象,所以它也是ContinueationImpl,所以会走ContinueationImpl的intercepted方法:

image.png 此处看context中有没有ContinuationInterceptor类型的Element,如果没有则返回自己,我们只要知道先返回自己。因为这个涉及到context的结构,后面再讲。

小节:
①、createCoroutine创建了一个SafeContinuation,并把CoroutineActivityonCreateonCreate1和一个标志位COROUTINE_SUSPENDED传入其中。CoroutineActivityonCreateonCreate1继承自SuspendLambda,并且是一个Function接口的实例。SuspendLambda继承自ContinuationImpl,ContinuationImpl继承自BaseContinuationImpl,BaseContinuationImpl继承自Continuation,CoroutineActivityonCreateonCreate1继承自SuspendLambda,也就是上面的协成要执行的闭包。CoroutineActivityonCreateonCreate1持有了CoroutineActivityonCreateonCreate2,它实现了Continuation。CoroutineActivityonCreateonCreate1重写了create方法,返回了一个新的CoroutineActivityonCreateonCreate1对象。
②、resume方法中会调用到SafeContinuation的resumeWith方法,最终会触发CoroutineActivityonCreateonCreate1的resumeWith方法。

协程执行

CoroutineActivityonCreateonCreate1继承自SuspendLambda,最终会继承自BaseContinuationImpl,来看下它的resumeWith:

image.png

resumeWith中首先调用invokeSuspend方法,如果invokeSuspend方法返回COROUTINE_SUSPENDED,则resumeWith直接不往下执行。否则看comppletion是不是BaseContinuationImpl,是的话,则继续轮训,直到comppletion不是BaseContinuationImpl,则执行它的resumeWith方法。此处的completion实际是CoroutineActivityonCreateonCreate2,所以会执行它的resumeWith方法。我们看下CoroutineActivityonCreateonCreate1的invokeSuspend方法:

image.png

它的返回值不是COROUTINE_SUSPENDED,所有上面的invokeSuspend方法还会继续往下执行。所以最终会执行了CoroutineActivityonCreateonCreate2,也就是例子中的匿名内部类的resumeWith方法。

此时我们再分析例子中日志的打印:

image.png

可以看到执行SafeContinuation的resumeWith的时候是一个while(true),传入的this.resume是一个COROUTINE_SUSPENDED标志位,所以会把CoroutineActivityonCreateonCreate1的resumeWith执行完后,才跳出while循环。因此日志最后输出协成外的代码。

类图总结

最后输出此次的类图结构,以作回顾:

协程类图.png