前置知识
CPS
Continuation Passing Style(续体传递风格): 约定一种编程规范,函数不直接返回结果值,而是在函数最后一个参数位置传入一个 callback 函数参数,并在函数执行完成时通过 callback 来处理结果。回调函数 callback 被称为续体(Continuation),它决定了程序接下来的行为,整个程序的逻辑通过一个个 Continuation 拼接在一起。
简单理解就是开发中我们常用的 callback 方式来接收返回的结果,只不过这里改名叫"续体"。
Kotlin 编译器处理协程体
我们用 kotlin 写协程代码在编译阶段会经过 kotlin 编译器处理,其会对协程相关逻辑进行拓展和调整。在接下来进行"通过实例源码解读"时,要知道如何查看 kotlin 协程代码编译。
kotlin
fun init(context: Context) {
MainScope().launch {
//...
}
}
如上面代码在经过 kotlin 编译器处理后会是什么样子呢?
- 首先将 kotlin 代码转成 bytecode

- 然后再将 bytecode 转成 java

得到的就是如下代码:
kotlin
public final void init(@NotNull final Context context) {
BuildersKt.launch$default(
CoroutineScopeKt.MainScope(),
(CoroutineContext)null,
(CoroutineStart)null,
(Function2)(new Function2((Continuation)null) {
//....
}),
3,
(Object)null);
}
可以看到,我们写的 MainScope().launch {}
被转换成了 BuildersKt.launch$default()
,其中第四个参数尤其关键,它就是我们的协程体(即 launch{} 部分的逻辑)。它是一个 Function2 的匿名对象,是不是看不懂?这里就好需要回到上一步(kotlin 转 bytecode)来看看。
标记 1、标记 2 共同完成了一件事,创建 FlutterManager <math xmlns="http://www.w3.org/1998/Math/MathML"> i n i t init </math>init1 对象,然后检查其类型是否为 Function2。用 java 伪代码表示
java
Object object = new FlutterManager$init$1(context, flutterSoConfig, continuation);
object instanceof Function2
标记3 就是上面 bytecode 转 java 后的代码块了,其中第四个入参为上面的 object。
可以发现 bytecode 中出现了转成 java 后没有的 FlutterManager <math xmlns="http://www.w3.org/1998/Math/MathML"> i n i t init </math>init1 类,其实它就是转成 java 后的 Function2,其继承自 SuspendLambda。可以继续在 bytecode 中验证这一说法。
Continuation 继承关系
java
- Continuation: 续体,恢复协程的执行
- BaseContinuationImpl: 实现 resumeWith(Result) 方法,控制状态机的执行,定义了 invokeSuspend 抽象方法
- ContinuationImpl: 增加 intercepted 拦截器,实现线程调度等
- SuspendLambda: 封装协程体代码块
- FlutterManager$init$1 协程体代码块生成的子类: 实现 invokeSuspend 方法,其内实现状态机流转逻辑
实例源解
kotlin
object FlutterManager{
fun init(context: Context) {
val flutterSoUrl = context.assets.open("flutterso.json").readBytes().decodeToString()
val flutterConfig =
Gson().fromJsonProxy(flutterSoUrl, FlutterSOConfig::class.java) ?: return
MainScope().launch {
val flutterSoFile = downloadDynamicSO(context, DownloadConfig(flutterConfig.libflutter.url, context.getDir("flutterso", Context.MODE_PRIVATE).absolutePath)
.apply {
fileName = FLUTTER_ENGINE_SO_NAME
})?.let { File(it) }
val appSoFile = downloadDynamicSO(context, DownloadConfig(flutterConfig.libapp.url, context.getDir("flutterso", Context.MODE_PRIVATE).absolutePath)
.apply {
fileName = FLUTTER_APP_SO_NAME
})?.let { File(it) }
if (flutterSoFile != null && appSoFile != null) {
loadAndInitFlutter(context, flutterSoFile.parentFile, appSoFile.absolutePath)
}
}
}
private suspend fun downloadDynamicSO(
context: Context,
downloadConfig: DownloadConfig
): String? {
return suspendCoroutine {
DownloadManager.instance.start(
context,
downloadConfig,
object : IDownloadListener {
override fun onSuccess(url: String?, savePath: Uri?) {
super.onSuccess(url, savePath)
it.resume(savePath?.path)
}
override fun onFailed(url: String?, throwable: Throwable) {
super.onFailed(url, throwable)
it.resumeWithException(throwable)
}
})
}
}
}
创建与启动
kotlin
//在示例代码中只传入了 block 参数,其他均采用默认值。
// bloack 即为协程体
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
//newContext = scope 上下文 + context 上下文 + Dispatchers
val newContext = newCoroutineContext(context)
// 默认参数,所以 coroutine = StandaloneCoroutine()
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block)
else
StandaloneCoroutine(newContext, active = true)
// 启动协程
// StandaloneCoroutine 继承自 AbstractCoroutine,这里调用的是父类 start()
coroutine.start(start, coroutine, block)
return coroutine
}
kotlin
//注意看!
// AbstractCoroutine 继承自 Continuation
// 所以 AbstractCoroutine 及子类(StandaloneCoroutine)都是 Continuation 对象,即续体!
public abstract class AbstractCoroutine<in T>
: JobSupport(active), Job, Continuation<T>, CoroutineScope {
/**
* @params start: CoroutineStart.DEFAULT
* @params receiver: StandaloneCoroutine
* @params block: suspend CoroutineScope.() -> Unit
*/
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
// 调用 CoroutineStart 的重载方法 invoke()
start(block, receiver, this)
}
}
kotlin
public enum class CoroutineStart {
DEFAULT,
//...
/**
* @params block: suspend CoroutineScope.() -> Unit
* @params receiver: StandaloneCoroutine
* @params completion: StandaloneCoroutine
*/
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
}
}
到这里可以知道默认参数情况下,调用 launch() 会先合并 context,然后创建 StandaloneCoroutine,其继承自 AbstractCoroutine,并持有合并后的 context,即包装一层成为续体(Continuation)。然后调用启动,最后执行到 (suspend R.() -> T)#startCoroutineCancellable()。
下面看真正的启动流程。
kotlin
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
receiver: R, completion: Continuation<T>,
onCancellation: ((cause: Throwable) -> Unit)? = null
) = runSafely(completion) {
//开始真正的启动逻辑
createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit), onCancellation)
}
createCoroutineUnintercepted()
kotlin
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
receiver: R,
completion: Continuation<T>
): Continuation<Unit> {
val probeCompletion = probeCoroutineCreated(completion)
// this 指 (suspend R.() -> T)
// 根据"前置知识 - Kotlin编译器处理线程体"、"前置知识 - Continuation继承关系"可知,
// this = SuspendLambda
// = ContinuationImpl
// = BaseContinuationImpl
// = Function2
// 即调用 Function2#create()
return if (this is BaseContinuationImpl)
create(receiver, probeCompletion)
else {
createCoroutineFromSuspendFunction(probeCompletion) {
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, it)
}
}
}
java
//下面代码为 MainScope().launch{} 解码后(kotlin -> bytecode ->java)的部分代码
BuildersKt.launch$default(CoroutineScopeKt.MainScope(), (CoroutineContext)null, (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
//....
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
//重新创建一个新的 SuspendLambda对象 (即新的 Continuation),其持有传入的续体(即第一层续体包装)
Function2 var3 = new <anonymous constructor>(completion);
return var3;
}
}), 3, (Object)null);
intercepted()
kotlin
//根据上面 createCoroutineUnintercepted() 可知,this = ContinuationImpl
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
(this as? ContinuationImpl)?.intercepted() ?: this
kotlin
internal abstract class ContinuationImpl : BaseContinuationImpl {
//执行逻辑如下:
// 1. intercepted != null,return 调度器包装续体。
// 2. intercepted == null,从 context 中提取 ContinuationInterceptor,
// 调用其 interceptContinuation(),生成调度器包装续体并 return。
// 3. intercepted == null && context 中没有 ContinuationInterceptor,则 return 原续体。
//
// 按照示例代码逻辑 MainScope(),其内部会注入 Dispatchers.Main(即 MainCoroutineDispatcher)
// 这里实际调用的是 MainCoroutineDispatcher#interceptContinuation()
public fun intercepted(): Continuation<Any?> =
intercepted
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
.also { intercepted = it }
}
kotlin
// MainCoroutineDispatcher 继承自 CoroutineDispatcher
// interceptContinuation() 由父类实现
public abstract class CoroutineDispatcher:ContinuationInterceptor {
//返回 DispatchedContinuation(即调度器包装续体),其持有调度器(这里指 MainCoroutineDispatcher)和第二层续体包装
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
}
resumeCancellableWith()
kotlin
public fun <T> Continuation<T>.resumeCancellableWith(
result: Result<T>,
onCancellation: ((cause: Throwable) -> Unit)? = null
): Unit = when (this) {
//根据上面拦截器处理逻辑,执行到这里!
// 即调用 DispatchedContinuation#resumeCancellableWith()
is DispatchedContinuation -> resumeCancellableWith(result, onCancellation)
else -> resumeWith(result)
}
kotlin
internal class DispatchedContinuation<in T>(
@JvmField val dispatcher: CoroutineDispatcher,
@JvmField val continuation: Continuation<T>
) : DispatchedTask<T>(MODE_UNINITIALIZED), CoroutineStackFrame, Continuation<T> by continuation {
/**
* @params result: Result.success(Unit)
* @parans onCancellation: null
*/
inline fun resumeCancellableWith(
result: Result<T>,
noinline onCancellation: ((cause: Throwable) -> Unit)?
) {
val state = result.toState(onCancellation)
//判断是否需要线程切换
// 根据示例代码 dispatcher = MainCoroutineDispatcher
// MainCoroutineDispatcher 的具体实现由 HandlerContext 来完成
// 继承关系:
// - MainCoroutineDispatcher
// - HandlerDispatcher
// *=> - HandlerContext
//
// 根据 HandlerContext#isDispatchNeeded() 逻辑 + 示例代码运行环境,此处为 false
if (dispatcher.isDispatchNeeded(context)) {
_state = state
resumeMode = MODE_CANCELLABLE
//调用调度器进行线程切换
// 如果需要切换的话,具体实现是由 HandlerContext#dispatch() 实现,
// 内部就是做了 Handler(Looper.getMainLooper()).post()。
// 传入的 this = DispatchedContinuation,其继承自 DispatchedTask,
// DispatchedTask 顶级父类为 Runnable,其内部重写 run(),最终调用到 continuation.resume()
dispatcher.dispatch(context, this)
} else {
executeUnconfined(state, MODE_CANCELLABLE) {
if (!resumeCancelled(state)) {
//最终调用到 continuation.resume()
resumeUndispatchedWith(result)
}
}
}
}
inline fun resumeUndispatchedWith(result: Result<T>) {
withContinuationContext(continuation, countOrElement) {
continuation.resumeWith(result)
}
}
}
启动逻辑代码很简短,只有一行,但却分了三步逻辑:
- 调用 createCoroutineUnintercepted() 创建第二层续体包装(SuspendLambda),其持有接受者和第一层续体包装(StandaloneCoroutine);
- 调用 intercepted(),进行拦截器(或者叫调度器)处理(例如,线程切换),此时经过处理后会生成第三层续体包装(DispatchedContinuation);
- 调用 resumeCancellableWith(),内部会根据调度器判断是否需要线程切换,无论是否切换,最终都会调用 continuation#resumeWith() 执行到协程体内部逻辑。
启动完结
我们接续往下看,上面分析到最后都会调用 continuation#resumeWith()。根据 DispatchedContinuation 的构造函数可知,resumeWith() 由第二层续体包装(SuspendLambda) 来实现,即 Kotlin 编译器处理生成的 Function2 实例。那么我们就看下其内部实现。
kotlin
//根据"前置知识 - Continuation继承关系"可知,
// resumeWith() 实际由 BaseContinuationImpl 实现
internal abstract class BaseContinuationImpl(
public val completion: Continuation<Any?>?
) : Continuation<Any?>, CoroutineStackFrame, Serializable {
public final override fun resumeWith(result: Result<Any?>) {
var current = this
var param = result
while (true) {
with(current) {
val completion = completion!!
val outcome: Result<Any?> =
try {
//执行协程体 invokeSuspend()
val outcome = invokeSuspend(param)
//挂起状态,直接 return
if (outcome === COROUTINE_SUSPENDED) return
//非挂起,直接返回结果
Result.success(outcome)
} catch (exception: Throwable) {
Result.failure(exception)
}
//如果需要,执行线程切换
releaseIntercepted()
if (completion is BaseContinuationImpl) {
current = completion
param = outcome
} else {
completion.resumeWith(outcome)
return
}
}
}
}
}
上面的代码是一套公共逻辑,我们先只关注启动后调用 resumeWith() 会怎么执行即可。首先会调用 invokeSuspend(),此方法也是在 Kotlin编译器生成的协程代码的 Function2 中实现。
java
BuildersKt.launch$default(CoroutineScopeKt.MainScope(), (CoroutineContext)null, (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
Object L$0;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
label40: {
// var10 = 挂起状态
Object var10 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (this.label) {
// 启动调用到此处!
case 0:
//判断传入的 result,如果为 Result.Failure,这直接抛异常。
// 按照上面启动逻辑,此处 resut = Result.success。
ResultKt.throwOnFailure($result);
//准备一些参数
//...
//将 label 标记为 1
this.label = 1;
//调用 downloadDynamicSO(), 传入函数定义的参数 + 续体
var10000 = var7.downloadDynamicSO(var8, var3, this);
//判断执行结果是否为挂起状态,是则直接 return,不阻塞当前线程执行。
// 根据示例代码,downloadDynamicSO() 为 suspend 修饰函数,所以此处应该为挂起状态。
if (var10000 == var10) {
return var10;
}
break;
//...
}
return Unit.INSTANCE;
}
//...
}), 3, (Object)null);
到这里,协程已经完成启动,并执行到第一个挂起函数,状态变为挂起状态。结合 BaseContinuationImpl#resumeWith() 逻辑,执行到挂起函数,并返回挂起状态,然后直接 return,启动阶段完结。
函数挂起与恢复
挂起
根据上面流程,逻辑执行到了挂起函数 downloadDynamicSO(),那么我们看下其怎么实现协程挂起与恢复的。
suspend 修饰 downloadDynamicSO(),其经过 Kotlin编译器生成如下代码:
java
//处理函数规定的入参外,最后额外追加了一个续体入参。
private final Object downloadDynamicSO(Context context, DownloadConfig downloadConfig, Continuation $completion) {
//根据启动时的经验,此处为创建 SafeContinuation 对象,其持有 DispatchedContinuation 对象,DispatchedContinuation 又持有 SuspendLambda 对象
SafeContinuation var5 = new SafeContinuation(IntrinsicsKt.intercepted($completion));
Continuation it = (Continuation)var5;
int var7 = false;
//调用下载,此处是一个异步操作
DownloadManager.Companion.getInstance().start(context, downloadConfig, (IDownloadListener)(new FlutterManager$downloadDynamicSO$2$1(it)));
//获取执行结果,由于是异步操作,所以此处返回结果为挂起
Object var10000 = var5.getOrThrow();
return var10000;
}
代码比较简单,主要逻辑为:
- 创建一个 SafeContinuation 续体包装,持有 DispatchedContinuation 对象,最终持有的为协程体续体对象(即 SuspendLambda)。
- 执行异步代码逻辑。
- 返回结果,此时结果为挂起。
Q: 代码中并没有体现何时为挂起状态
A: 状态的管理由新创建的续体(SafeContinuation)处理。我们来看下它的内部实现,便可知上面代码在调用异步逻辑后,接着调用 SafeContinuation#getOrThrow() 返回的就是 COROUTINE_SUSPENDED。
kotlin
internal actual class SafeContinuation<in T>
internal actual constructor(
private val delegate: Continuation<T>,
initialResult: Any?
) : Continuation<T>, CoroutineStackFrame {
//未携带状态的构造函数,默认状态为 UNDECIDED
internal actual constructor(delegate: Continuation<T>) : this(delegate, UNDECIDED)
// 构造时未赋值状态时,默认初始状态为 UNDECIDED
@Volatile
private var result: Any? = initialResult
internal actual fun getOrThrow(): Any? {
var result = this.result // atomic read
//如果默认状态,则更新状态为 COROUTINE_SUSPENDED
if (result === UNDECIDED) {
if (RESULT.compareAndSet(this, UNDECIDED, COROUTINE_SUSPENDED)) return COROUTINE_SUSPENDED
result = this.result // reread volatile var
}
//状态更新
return when {
result === RESUMED -> COROUTINE_SUSPENDED // already called continuation, indicate COROUTINE_SUSPENDED upstream
result is Result.Failure -> throw result.exception
else -> result // either COROUTINE_SUSPENDED or data
}
}
}
恢复
Q: 上面异步逻辑执行完成后(即下载完成),代码如何继续执行的呢?
A: 核心点就在下载完成后调用 continuation.resume()。我们继续来看下 downloadDynamicSO 经 Kotlin 编译器处理后生成的类。
java
private final Object downloadDynamicSO(Context context, DownloadConfig downloadConfig, Continuation $completion) {
//...
//调用下载,最后注入一个下载监听对象,该对象持有 SafeContinuation 续体对象
DownloadManager.Companion.getInstance().start(
context,
downloadConfig,
(IDownloadListener)(new FlutterManager$downloadDynamicSO$2$1(it)));
Object var10000 = var5.getOrThrow();
return var10000;
}
public final class FlutterManager$downloadDynamicSO$2$1 implements IDownloadListener {
FlutterManager$downloadDynamicSO$2$1(Continuation $captured_local_variable$0) {
this.$it = $captured_local_variable$0;
}
final Continuation $it;
public void onSuccess(@Nullable String url, @Nullable Uri savePath) {
super.onSuccess(url, savePath);
Continuation var3 = this.$it;
String var4 = savePath != null ? savePath.getPath() : null;
//下载完成,调用 SafeContinuation#resumeWith()
var3.resumeWith(Result.constructor-impl(var4));
}
//...
}
kotlin
internal actual class SafeContinuation<in T>
internal actual constructor(
private val delegate: Continuation<T>,
initialResult: Any?
) : Continuation<T>, CoroutineStackFrame {
//未携带状态的构造函数,默认状态为 UNDECIDED
internal actual constructor(delegate: Continuation<T>) : this(delegate, UNDECIDED)
// 构造时未赋值状态时,默认初始状态为 UNDECIDED
@Volatile
private var result: Any? = initialResult
public actual override fun resumeWith(result: Result<T>) {
while (true) {
//结合挂起小节中的逻辑,此时 result = COROUTINE_SUSPENDED
val cur = this.result
when {
cur === UNDECIDED -> if (RESULT.compareAndSet(this, UNDECIDED, result.value)) return
cur === COROUTINE_SUSPENDED -> if (RESULT.compareAndSet(this, COROUTINE_SUSPENDED, RESUMED)) {
//执行到此处!!
// 结合挂起小节中的逻辑,此处调用的是 DispatchedContinuation#resumeWith()
delegate.resumeWith(result)
return
}
else -> throw IllegalStateException("Already resumed")
}
}
}
}
逻辑执行到此处,最后调用的是 DispatchedContinuation#resumeWith(),该方法与"创建与启动-resumeCancellableWith"小节中的 DispatchedContinuation#resumeCancellableWith() 实现逻辑基本一致,不再重复解读,最终执行到协程体的 invokeSuspend() 逻辑。
java
BuildersKt.launch$default(CoroutineScopeKt.MainScope(), (CoroutineContext)null, (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
Object L$0;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
label40: {
// var10 = 挂起状态
Object var10 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (this.label) {
case 0:
//将 label 标记为 1
this.label = 1;
//...
break;
//代码再次执行到 invokeSuspend(),此时 label = 1
case 1:
//获取到第一个挂起函数返回的结果,并赋值给 var10000
ResultKt.throwOnFailure($result);
var10000 = $result;
break;
//...
}
var16 = (String)var10000;
//执行后续示例代码中 .let{} 部分的逻辑
//...
this.label = 2;
//调用下一个挂起函数
var10000 = var7.downloadDynamicSO(var8, var4, this);
if (var10000 == var10) {
return var10;
}
return Unit.INSTANCE;
}
//...
}), 3, (Object)null);
回到 invokeSuspend() 逻辑中,由于第一次挂起时将 label 标记为 1,所以此时执行 case 1 的逻辑,将结果赋值给 var1000,break 跳出 switch,继续执行后续逻辑,最后将 label 标记为 2,调用下一个挂起函数。后面的逻辑又回到了函数的挂起与恢复,和上面解析的一致,这里就不重复了。
总结
博文来自青杉