Kotlin协程从1.3正式版除出来也很久了,相比大家伙也比较熟悉了,从Android的AAC架构到后后端都可以见到它的身影,那么问题来了,用了那么久的协程体你知道它怎么创建的么。
一天我问同事:你知不知道协程是怎么创建的?
同事:我知道,是launch
,async
,...
。
我:?????
我:那如果抛开官方给的框架,就用Kotlin语言提供的最基本的API创建呢?
同事:????,那你创个毛。
此时人送外号逼王的我,知道机会来了。于是乎就有了这个文章。
预备知识
首先,你需要知道协程是啥玩意,知道基本概念之后在来看(知道的话,当我没有说
好戏开场了
我们抛开官方的框架,只用语言最基本的API来洞悉Kotlin协程的内部设计。
为了接下来方便带领大家分析,我把用到的概念和API给贴出来,方便分析。
一般启动协程我们需要:
1、一个suspend fun
suspend fun foo() {}
复制代码
2、一个启动协程的API:startCoroutine,如果不想要立即执行,则可用:createCoroutine
@SinceKotlin("1.3")
@Suppress("UNCHECKED_CAST")
public fun <T> (suspend () -> T).startCoroutine(
completion: Continuation<T>
) {
createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}
复制代码
@SinceKotlin("1.3")
@Suppress("UNCHECKED_CAST")
public fun <T> (suspend () -> T).createCoroutine(
completion: Continuation<T>
): Continuation<Unit> =
SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)
复制代码
上面创建协程和启动协程中我们看到有一个非常关键的方法:createCoroutineUnintercepted
方法实现如下(具体功能稍后在说:
@SinceKotlin("1.3")
public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
completion: Continuation<T>
): Continuation<Unit> {
val probeCompletion = probeCoroutineCreated(completion)
return if (this is BaseContinuationImpl)
create(probeCompletion)
else
createCoroutineFromSuspendFunction(probeCompletion) {
(this as Function1<Continuation<T>, Any?>).invoke(it)
}
}
复制代码
3、协程执行完后需要一个 completion 来回调,即: Continuation
@SinceKotlin("1.3")
public interface Continuation<in T> {
/**
* The context of the coroutine that corresponds to this continuation.
*/
public val context: CoroutineContext
/**
* Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the
* return value of the last suspension point.
*/
public fun resumeWith(result: Result<T>)
}
复制代码
4、BaseContinuationImpl
@SinceKotlin("1.3")
internal abstract class BaseContinuationImpl(
// This is `public val` so that it is private on JVM and cannot be modified by untrusted code, yet
// it has a public getter (since even untrusted code is allowed to inspect its call stack).
public val completion: Continuation<Any?>?
) : Continuation<Any?>, CoroutineStackFrame, Serializable {
// This implementation is final. This fact is used to unroll resumeWith recursion.
public final override fun resumeWith(result: Result<Any?>) {
// This loop unrolls recursion in current.resumeWith(param) to make saner and shorter stack traces on resume
var current = this
var param = result
while (true) {
// Invoke "resume" debug probe on every resumed continuation, so that a debugging library infrastructure
// can precisely track what part of suspended callstack was already resumed
probeCoroutin