抽丝剥茧聊Kotlin协程之协程启动原理

1,503 阅读3分钟

1. 前言

本文主要介绍协程的启动原理,如果没有协程实战经验或者没有Debug过协程的源码,看起来可能会有点费劲,但是如果将来有机会学习协程的源码,那么这篇文章可能会对你有一定的帮助。文章主要是讲解协程启动的流程,简便起见,该过程中涉及到的其它比较重要协程知识点可能只会简单提到,本文不会深入去讲解,后续有空会对他们单独成文。

2. 简单的例子

首先 我们来看一个简单的例子,在主线程中开启一个协程,打印“Hello Coroutines”。通过这个简单的例子讲解协程是如何启动起来的。

接着我们将startCoroutines方法反编译成Java文件

我们看到Kotlin代码中的Lambda表达式变成了Java中的Function2对象,而它对应的具体类是SuspendLambda。 我们注意到它实现了三个方法:

  1. Object invokeSuspend(Object var1)
  2. Continuation create(Object value,Continuation completion)
  3. 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

该方法功能如下:

  1. 为新协程创建一个新的CoroutineContext
  2. 本文Case创建StandaloneCoroutine
  3. 调用coroutine.start方法启动协程

该方法功能如下:

  1. 调用initParentJob方法,与父Job建立关联,当调用cancel方法或者子Job有异常时,可以将取消或者异常事件往上传播(本文暂且忽略,简单了解就好了
  2. 调用start(block, receiver, this),该方法超级有迷惑性,它真正调用的是CoroutineStart的invoke方法。

3.2 CoroutineStart.invoke

本案例中执行DEFAULT分支

3.3 (suspend(R)->T).startCoroutineCancellable

最终是通过suspend方法的startCoroutineCancellable方法来启动协程的,本文对应的是lambda,前文我们讲过它对应的是SuspendLambda

{
  println("Hello Coroutines")
}

该方法包含了几个重要方法的调用:

  1. (suspend R.()->T).createCoroutineUnintercepted
  2. Continuation.intercepted()
  3. 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启动协程的过程与它又完全不同。 记得关注“字节小站”微信公众号哟~