在上一篇文章 kotlin 协程入门教程 中,讲过协程本质是线程池的Task。本篇文章就从源码的角度,来看看协程任务是怎么一步一步被启动的。
协程启动流程
如果你只对协程启动流程感兴趣,但不想看源码分析,直接看下面的kotlin协程启动的流程图就好。如果对源码分析感兴趣,可以继续往下看源码分析的部分。
源码分析
首先我们先看一下 launch 扩展方法的实现
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true) //注释1
coroutine.start(start, coroutine, block)
return coroutine
}
CoroutineStart 是指协程的启动选项,默认是 CoroutineStart.DEFAULT。即默认情况下,代码会走到注释1的位置,然后会调用 StandaloneCoroutine 的 start 方法。
private open class StandaloneCoroutine(
parentContext: CoroutineContext,
active: Boolean
) : AbstractCoroutine<Unit>(parentContext, initParentJob = true, active = active) {
override fun handleJobException(exception: Throwable): Boolean {
handleCoroutineException(context, exception)
return true
}
}
而 StandaloneCoroutine 是继承 AbstractCoroutine 的,调到的是它的 start 方法。源码如下:
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
start(block, receiver, this)
}
到这里还没有结束,AbstractCoroutine 的 start 方法内部又调到了 CoroutineStart 的 invoke 方法。这里的 start(block, receiver, this) 其实等同于 start.invoke(block, receiver, this),这是kotlin中的invoke 约定。
@InternalCoroutinesApi
public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit =
when (this) {
DEFAULT -> block.startCoroutineCancellable(receiver, completion)
ATOMIC -> block.startCoroutine(receiver, completion)
UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
LAZY -> Unit // will start lazily
}
在 invoke 方法中,我们能看到最后是调到了 startCoroutineCancellable 方法。它其实是挂起函数的的扩展函数。函数源码如下所示:
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
receiver: R, completion: Continuation<T>,
onCancellation: ((cause: Throwable) -> Unit)? = null
) =
runSafely(completion) {
createCoroutineUnintercepted(receiver, completion) //注释1
.intercepted() //注释2
.resumeCancellableWith(Result.success(Unit), onCancellation) //注释3
}
首先是注释1的 createCoroutineUnintercepted 方法,从它的方法名可以看出它会创建协程实例。不过你看它的方法原型,可以看到它返回了 Continuation 对象。那 Continuation 是什么东西呢? Continuation 是挂起函数实现挂起和恢复功能的核心接口,后面的文章介绍挂起函数时,再详细介绍。这里你只需要把 Continuation 理解成一个携带逻辑的callback就可以了。
launch {
doSomething()
}
//等价于
val callback = Callback {
doSomething()
}
注释2的 intercepted 方法则是找到 CoroutineContext 中的 ContinuationInterceptor,并调用它的 intercepted 方法。如果是是第一次调用,则会调用它的 interceptContinuation 方法。
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
(this as? ContinuationImpl)?.intercepted() ?: this
// ContinuationImpl 的 intercepted 方法
public fun intercepted(): Continuation<Any?> =
intercepted
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
.also { intercepted = it }
CoroutineDispatcher 实现了 ContinuationInterceptor 的接口,之前在kotlin 协程入门教程中介绍过,CoroutineDispatcher 的功能是将协程任务分发到要求的线程上。其中它的 interceptContinuation 方法就是返回一个 DispatchedContinuation 对象。
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
如上图所示,DispatchedContinuation 的本质其实就是一个 Runnable。在创建 DispatchedContinuation 时会传入continuation 和 this, 其中 this 是指 CoroutineDispatcher。
CoroutineDispatcher有四个重要的子类,分别是HandlerContext、DefaultIoScheduler、DefaultScheduler和Unconfined。其中 HandlerContext 对应 Dispatchers.Main;DefaultIoScheduler 对应 Dispatchers.IO ; DefaultScheduler 对应于 Dispatchers.Default;Unconfined 对应于 Dispatchers.Unconfined。
从源码中可以看到,在注释1和注释2的方法中,我们都是在做准备工作。那么启动协程的核心就在注释3的方法中了。在看注释3处的方法之前,我们需要先看一下 CoroutineContext 是什么时候被创建的?
还记得在kotlin 协程入门教程中讲过:所有协程都需要通过 CoroutineScope 来启动。其实,CoroutineContext 和 ContinuationInterceptor 的创建时机就在这里。下面我们通过源码来证明。
public fun CoroutineScope.launch(
//当我们没有传入 CoroutineContext 时,会使用 EmptyCoroutineContext 作为默认传入值
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
//这里会根据旧的 CoroutineContext,来创建新的CoroutineContext
val newContext = newCoroutineContext(context)
...
}
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
// newCoroutineContext 是 CoroutineScope 的扩展方法,其中的
// coroutineContext 就是 CoroutineScope 的属性
val combined = foldCopies(coroutineContext, context, true)
...
}
//这里以最常用的 lifecycleScope 来看看它内部是怎么创建 CoroutineContext 的
public val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
get() = lifecycle.coroutineScope // 返回了 coroutineScope,继续往下看
public val Lifecycle.coroutineScope: LifecycleCoroutineScope
get() {
while (true) {
...
val newScope = LifecycleCoroutineScopeImpl(
this,
// 核心代码在这里
SupervisorJob() + Dispatchers.Main.immediate
)
...
}
}
说回注释3的 resumeCancellableWith 方法。上面提到过 intercepted 返回一个 DispatchedContinuation 对象,因此会调用到 DispatchedContinuation 对象的 resumeCancellableWith 方法。
public fun <T> Continuation<T>.resumeCancellableWith(
result: Result<T>,
onCancellation: ((cause: Throwable) -> Unit)? = null
): Unit = when (this) {
is DispatchedContinuation -> resumeCancellableWith(result, onCancellation)
else -> resumeWith(result)
}
在 DispatchedContinuation 中,resumeCancellableWith 会根据 dispatcher.isDispatchNeeded 来判断是否是否分发(dispatch)协程。通过上面的分析,我们知道 dispatcher 其实是 CoroutineDispatcher 的子类,默认情况下 CoroutineDispatcher 的 isDispatchNeeded 只返回 true。当 dispatcher 为 Unconfined ; 或者 dispatcher 为 HandlerContext ,同时创建的和执行的线程的 looper 不一致时,才会返回 false。
internal inline fun resumeCancellableWith(
result: Result<T>,
noinline onCancellation: ((cause: Throwable) -> Unit)?
) {
val state = result.toState(onCancellation)
if (dispatcher.isDispatchNeeded(context)) {
_state = state
resumeMode = MODE_CANCELLABLE
dispatcher.dispatch(context, this)
} else {
executeUnconfined(state, MODE_CANCELLABLE) {
if (!resumeCancelled(state)) {
resumeUndispatchedWith(result)
}
}
}
}
当 dispatcher 为 HandlerContext 时,最后会执行 hander.post,这个就不多介绍了。当 dispatcher 为 Unconfined 时,走到第二个条件语句,这时协程的执行不会分发到其他的线程,只在当前线程执行。当 dispatcher 为 DefaultIoScheduler 或者 DefaultScheduler 时,最后执行的都是 CoroutineScheduler 的 dispatch 方法。源码如下:
fun dispatch(block: Runnable, taskContext: TaskContext = NonBlockingContext, tailDispatch: Boolean = false) {
...
//实际上是创建一个 TaskImpl 对象
val task = createTask(block, taskContext)
//获取当前 Worker,Worker 继承 Thread,本质是一个线程
val currentWorker = currentWorker()
//将Task添加到 Worker 线程本地队列
val notAdded = currentWorker.submitToLocalQueue(task, tailDispatch)
...
}
internal class TaskImpl(
@JvmField val block: Runnable,
submissionTime: Long,
taskContext: TaskContext
) : Task(submissionTime, taskContext) {
override fun run() {
try {
//这里实际上是执行 DispatchedContinuation 的 run 方法
block.run()
} finally {
taskContext.afterTask()
}
}
}
internal inner class Worker private constructor() : Thread() {
override fun run() = runWorker()
private fun runWorker() {
var rescanned = false
while (!isTerminated && state != WorkerState.TERMINATED) {
val task = findTask(mayHaveLocalTasks)
if (task != null) {
rescanned = false
minDelayUntilStealableTaskNs = 0L
//执行任务
executeTask(task)
continue
} else {
mayHaveLocalTasks = false
}
...
}
private fun executeTask(task: Task) {
..
runSafely(task)
...
}
fun runSafely(task: Task) {
try {
//最终是调用 task 的run 方法
task.run()
} catch (e: Throwable) {
val thread = Thread.currentThread()
thread.uncaughtExceptionHandler.uncaughtException(thread, e)
} finally {
unTrackTask()
}
}
}
dispatch 方法首先会创建一个 TaskImpl 对象,它内部的 run 方法会调用 DispatchedContinuation 的 run 方法。第二步则会获取 Worker,它继承 Thread,本质是一个线程。第三步则将创建的 Task 添加到 Worker 线程本地队列中去。
Worker 线程会不断从线程本地队列中获取 Task,经过 executeTask -> runSafely 方法,最后会调用 Task 的 run方法,最后调用到 DispatchedContinuation 的 run 方法,这时就完成了协程的启动。