1. 前言
本文专注于讲解MainScope启动协程的整体流程。由于时间和篇幅关系,对于文中的一些前置知识点不会过多讲解。希望本文能给读者对于协程的启动有个基本的了解。要想真正理解启动原理,还需要亲力亲为,跟着文章Debug一番。
2. 简单的例子
首先
我们来看一个简单的例子,在主线程中开启一个协程,打印“Hello Coroutines”。通过这个简单的例子讲解协程是如何启动起来的。
接着
我们将startCoroutines方法反编译成Java文件
我们看到Kotlin代码中的Lambda表达式变成了Java中的Function2对象,而它对应的具体类是SuspendLambda。我们注意到它实现了三个方法:
- Object invokeSuspend(Object var1)
- Continuation create(Object value,Continuation completion)
- Object invoke(Object var1,Object var2)
大家仔细看就会发现,invokeSuspend和create方法定义在BaseContinuationImpl中,invoke方法定义Function2中。而SuspendLambda继承了ContinuationImpl同时又实现了SuspendFunction接口。
在Kotlin中,方法也是对象,一个参数的方法对应的类是Function1,以此类推两个参数方法对应的是Function2。
行文至此,有很多新东西,suspend、Continuation、CoroutineScope、CoroutineContext,但是本文不会讲解,接着往下看协程启动过程吧。
3. 启动过程
3.1 CoroutineScope.launch
该方法功能如下:
- 为新协程创建一个新的CoroutineContext
- 本文Case创建StandaloneCoroutine
- 调用coroutine.start方法启动协程
该方法功能如下:
- 调用initParentJob方法,与父Job建立关联,当调用cancel方法或者子Job有异常时,可以将取消或者异常事件往上传播(本文暂且忽略,简单了解就好了)
- 调用start(block, receiver, this),该方法超级有迷惑性,它真正调用的是CoroutineStart的invoke方法。
3.2 CoroutineStart.invoke
本案例中执行DEFAULT分支
3.3 (suspend(R)->T).startCoroutineCancellable
最终是通过suspend方法的startCoroutineCancellable方法来启动协程的,本文对应的是lambda,前文我们讲过它对应的是SuspendLambda
{ println("Hello Coroutines") }
该方法包含了几个重要方法的调用:
- (suspend R.()->T).createCoroutineUnintercepted
- Continuation.intercepted()
- Continuation.resumeCancellableWith
3.4 (suspend R.() -> T).createCoroutineUnintercepted
当前的this是启动协程的闭包,前面我们通过反编译startCoroutines方法,发现闭包实现了create方法,此处调用的正是反编译后生成的create方法
参数completion对应的是协程StandaloneCoroutine,此处生成的Function2对象也是SuspendLambda对象
3.5 Continuation.intercepted()
该方法的主要作用就是生成DispatchedContinuation。它由CoroutineDispatcher和Continuation两部分组成。Dispatcher决定在Continuation在哪个线程中执行,本文是在主线程
3.6 Continuation.resumeCancellableWith
3.7 MainScope
Dispatchers.Main 最终由kotlinx.coroutines.android.AndroidDispatcherFactory
创建
3.8 AndroidDispatcherFactory
最终通过Handler.post方法把DispatchedContinuation放入主线程消息队列
3.9 DispatchedTask.run
Android主线程调度,最终会调用到DispatchedTask.run方法中,通过continuation.resume方法执行协程体。
3.10 BaseContinuationImpl.resumeWith
ContinuationImpl.kt
该方法循环调用Continuation的invokeSuspend方法,直到当前completion是协程本身会跳出循环。
真正执行打印“Hello Coroutines”
3.11 AbstractCoroutine.resumeWith
协程执行完成
表示协程执行结束,它可能需要等待子协程结束,结束后需要告知父协程
4. 总结
协程的前置知识点太多了,一篇文章无法解释清楚,有疑问可以在评论区提出。本文主要是讲解了Dispatchers.Main启动协程的过程。Dispatchers.Default启动协程的过程与它又完全不同。