Featured image of post Android 协程启动到执行

Android 协程启动到执行

协程创建

demo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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方法传入了两个参数,我们都知道扩展函数最终编译出来的方法第一个参数是被扩展对象,所以此处的CoroutineActivity$onCreate$1就是(suspend () -> T),CoroutineActivity$onCreate$2对应的是例子中的Continuation匿名内部类。我们注意下,此时传入CoroutineActivity$onCreate$1中的Continuation参数是null。

image.png

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

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

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

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

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

我们再来看intercepted方法。

intercepted

image.png

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

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

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

协程执行

CoroutineActivity$onCreate$1继承自SuspendLambda,最终会继承自BaseContinuationImpl,来看下它的resumeWith:

image.png

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

image.png

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

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

image.png

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

类图总结

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

协程类图.png

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy