Kotlin协程源码分析-4 状态机
今天就来分析所谓的协程
的状态机
首先来看一个问题,我们知道suspend
函数会在编译后生成一个传入continuation
的函数。
那么我们如何自己拿到这个传入的对象呢?这个对象又有什么作用?
请看如下代码即可
//com.example.studycoroutine.chapter.four.MyCoroutine.kt
suspend fun commonSuspendFun(): String {
return "[hello world] "
}
suspend fun commonSuspendFun2(): String {
return "[hello world 2] "
}
suspend fun commonSuspendFun3(): String {
return "[hello world 3]"
}
fun testFunGetContinuation() {
val mystartSuspend: suspend () -> String = {
//返回一个字符串hello world
val one = commonSuspendFun()
//返回一个字符串hello world2
val two = commonSuspendFun2()
//返回一个字符串hello world3
val three = commonSuspendFun3()
one+two+three
}
val myCoroutine = MyCoroutine()
//mystartSuspend.createCoroutine本质也是下面的调用不过封装了代理,不方便我们学习
val createCoroutine = mystartSuspend.createCoroutineUnintercepted(myCoroutine)
createCoroutine.resume(Unit)
}
//com.example.studycoroutine.chapter.two.MyCoroutine.kt
class MyCoroutine() : Continuation<String> {
override fun resumeWith(result: Result<String>) {
logD("MyCoroutine 回调resumeWith 返回的结果 " + result.getOrNull())
}
override val context: CoroutineContext
get() = kotlin.coroutines.EmptyCoroutineContext
}
输出结果:
[main] MyCoroutine 回调resumeWith 返回的结果 [hello world] [hello world 2] [hello world 3]
想要弄懂就必须从编译后的字节码入手.
反编译结果结构图:
MyCoroutineKt.testFunGetContinuation.mystartSuspend.1
对应我们的suspend lambda
对象mystartSuspend
。而这个对象主要作用就是用来维护 状态机
,这个也懂kotlin
协程的关键。
MyCoroutineKt.testFunGetContinuation.mystartSuspend.1
被我改名为MystartSuspend
,方便查看编译后的源码,下面的反编译字节码翻译成java
语法,部分被我修饰了。
class MystartSuspend extends SuspendLambda implements Function1 {
Object saveResultOne;
Object saveResultTwo;
int label;
MystartSuspend(Continuation continuation) {
super(1, continuation);
}
public final Continuation create(Continuation continuation) {
return new MystartSuspend(continuation);
}
public final Object invoke(Object continuation) {
return ((MystartSuspend) this.create(((Continuation) continuation))).invokeSuspend(Unit.INSTANCE);
}
public final Object invokeSuspend(Object $result) {
Object invokeResult;
Object v3;
String v1_2;
Object resultThree;
String resultOne;
String resultTwo;
Object SUSPEND_FLAG = IntrinsicsKt.getCOROUTINE_SUSPENDED();
int v1 = this.label;
if (v1 == 0) {
ResultKt.throwOnFailure($result);
this.label = 1;
invokeResult = MyCoroutineKt.coMystartSuspendonSuspendFun(((Continuation) this));
if (invokeResult == SUSPEND_FLAG) {
return SUSPEND_FLAG;
}
label_40:
v1_2 = (String) invokeResult;
this.saveResultOne = v1_2;
this.label = 2;
v3 = MyCoroutineKt.coMystartSuspendonSuspendFun2(((Continuation) this));
if (v3 == SUSPEND_FLAG) {
return SUSPEND_FLAG;
}
label_46:
String two = (String) v3;
this.saveResultOne = v1_2;
this.saveResultTwo = two;
this.label = 3;
resultThree = MyCoroutineKt.coMystartSuspendonSuspendFun3(((Continuation) this));
if (resultThree == SUSPEND_FLAG) {
return SUSPEND_FLAG;
}
resultOne = v1_2;
resultTwo = two;
} else {
if (v1 != 1) {
if (v1 != 2) {
if (v1 == 3) {
resultTwo = (String) this.saveResultTwo;
resultOne = (String) this.saveResultOne;
ResultKt.throwOnFailure($result);
resultThree = $result;
return resultOne + resultTwo + (((String) resultThree));
}
throw new IllegalStateException("call to \'resume\' before \'invoke\' with coroutine");
}
v1_2 = (String) this.saveResultOne;
ResultKt.throwOnFailure($result);
v3 = $result;
goto label_46;
}
ResultKt.throwOnFailure($result);
invokeResult = $result;
goto label_40;
}
return resultOne + resultTwo + (((String) resultThree));
}
}
调用类反编译
public final class MyCoroutineKt {
public static final Object commonSuspendFun(Continuation $completion) {
return "[hello world] ";
}
public static final Object commonSuspendFun2(Continuation $completion) {
return "[hello world 2] ";
}
public static final Object commonSuspendFun3(Continuation $completion) {
return "[hello world 3]";
}
public static final void testFunGetContinuation() {
ContinuationKt.createCoroutine(((Function1)new MystartSuspend(null)), ((Continuation)new MyCoroutine())).resumeWith(Result.constructor-impl(Unit.INSTANCE));
}
}
首先我们要知道创建我们的代码调用createCoroutineUnintercepted
调用返回的对象是什么。
//kotlin.coroutines.intrinsics.IntrinsicsJvm.kt
public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
completion: Continuation<T>
): Continuation<Unit> {
//返回myCoroutine对象
val probeCompletion = probeCoroutineCreated(completion)
//if会返回true 上一篇文章有讲过lambda会编译成BaseContinuationImpl类
//this 就是我们的mystartSuspend对象,也就是MystartSuspend类
return if (this is BaseContinuationImpl)
//调用MystartSuspend的create方法,最终返回MystartSuspend实例
create(probeCompletion)
else
createCoroutineFromSuspendFunction(probeCompletion) {
(this as Function1<Continuation<T>, Any?>).invoke(it)
}
}
综上所述createCoroutineUnintercepted
返回MystartSuspend
对象实例。
在返回MystartSuspend
后我们调用resume
启动协程。MystartSuspend
是一个Continuation
,调用resume
的函数声明,发现是一个扩展函数。
public inline fun <T> Continuation<T>.resume(value: T): Unit =
resumeWith(Result.success(value))
经过继承链会调用到BaseContinuationImpl
对象上的resumeWith
方法
//kotlin.coroutines.jvm.internal.BaseContinuationImpl.kt
internal abstract class BaseContinuationImpl(
public val completion: Continuation<Any?>?//我们传入myCoroutine对象
) : Continuation<Any?>, CoroutineStackFrame, Serializable {
public final override fun resumeWith(result: Result<Any?>) {
var current = this
var param = result
while (true) {
//先无视探针相关内容
probeCoroutineResumed(current)
with(current) {
//用于快速错误监测有没有传入completion对象
val completion = completion!!
val outcome: Result<Any?> =
try {
//调用子类的状态机函数,这部分往往有编译器实现
//当然你也可以自己实现。在我们的MystartSuspend类中
//invokeSuspend函数你就看到了
val outcome = invokeSuspend(param)
//如果状态机返回的挂起标识那么退出循环
//等候下一次调用Continuation的resumeWith函数调用在进入
if (outcome === COROUTINE_SUSPENDED) return
Result.success(outcome)
} catch (exception: Throwable) {
Result.failure(exception)
}
releaseIntercepted()
if (completion is BaseContinuationImpl) {
current = completion
param = outcome
} else {
//如果状态返回了结果二部是挂起标识那么结束全部流程,
//并且回调我么demo中MyCoroutine对象
completion.resumeWith(outcome)
return
}
}
}
}
}
综上所述我么总结一下流程:
- 步骤一:我们创建一个
Continuation
对象用户接受挂起函数完成的通知。本案例为MyCoroutine
- 步骤二:创建一个
suspend lambda
。本案例为mystartSuspend
- 步骤三:
mystartSuspend
内部调用其他挂起函数 - 步骤四:
createCoroutineUnintercepted
创建一个新的Continuation
这个Continuation
对象是步骤二的suspend lambda
所生成的(编译器会生成一个SuspendLambda
对象) - 步骤五:调用
suspend lambda
的continuation
的onresume
. - 步骤六:
suspend lambda
的continuation
的父类BaseContinuationImpl
内部resumeWith
被回调 - 步骤七:父类
BaseContinuationImpl
内部resumeWith
内部调用子类invokeSuspend
并获取结果 - 步骤八:子类
invokeSuspend
调用内部的挂起函数,并把自己作为continuation
传入 - 步骤九:获取
invokeSuspend
返回结果。如果返回不是COROUTINE_SUSPENDED
,协程结束回调监听完成的continuation
类(本案例中MyCoroutine
被回调)。如果返回COROUTINE_SUSPENDED
那么结束父类BaseContinuationImpl
内部resumeWith
的死循环,并下一次等候回调resumeWith
(因为子类的invokeSuspend
中调用挂起函数传入的自己(continuation)给挂起函数,挂起函数完成时回调他的resumeWith
方法) - 步骤十:重复步骤八和九。直到返回结果
我们这里回过头来看看编译器帮我生成类(请看注释)
class MystartSuspend extends SuspendLambda implements Function1 {
Object saveResultOne;
Object saveResultTwo;
int label;
public final Object invokeSuspend(Object $result) {
Object invokeResult;
Object v3;
String v1_2;
Object resultThree;
String resultOne;
String resultTwo;
Object SUSPEND_FLAG = IntrinsicsKt.getCOROUTINE_SUSPENDED();
// (一)状态lable为0
int v1 = this.label;
if (v1 == 0) {
ResultKt.throwOnFailure($result);
// (二)状态lable为1
this.label = 1;
// (三) 调用第一个挂起函数,传入自己做为参数,并检查返回值
//如果返回SUSPEND_FLAG就返回函数。当挂起函数用传入的Continuation
//调用resume时候又重新回到这个invokeSuspend方法
//当然我们这里由于不会返回SUSPEND_FLAG 所以继续向下运行
invokeResult = MyCoroutineKt.coMystartSuspendonSuspendFun(((Continuation) this));
if (invokeResult == SUSPEND_FLAG) {
return SUSPEND_FLAG;
}
label_40:
v1_2 = (String) invokeResult;
this.saveResultOne = v1_2;
//(四) 更新label状态为2
this.label = 2;
//(五) 调用挂起函数 与(三)相同
v3 = MyCoroutineKt.coMystartSuspendonSuspendFun2(((Continuation) this));
if (v3 == SUSPEND_FLAG) {
return SUSPEND_FLAG;
}
label_46:
String two = (String) v3;
this.saveResultOne = v1_2;
this.saveResultTwo = two;
//(六) 更新lable为3
this.label = 3;
// (七) 调用最后一个挂起函数 同样与(三)一样检查结果
resultThree = MyCoroutineKt.coMystartSuspendonSuspendFun3(((Continuation) this));
if (resultThree == SUSPEND_FLAG) {
return SUSPEND_FLAG;
}
resultOne = v1_2;
resultTwo = two;
} else {
if (v1 != 1) {
if (v1 != 2) {
if (v1 == 3) {
resultTwo = (String) this.saveResultTwo;
resultOne = (String) this.saveResultOne;
ResultKt.throwOnFailure($result);
resultThree = $result;
return resultOne + resultTwo + (((String) resultThree));
}
throw new IllegalStateException("call to \'resume\' before \'invoke\' with coroutine");
}
v1_2 = (String) this.saveResultOne;
ResultKt.throwOnFailure($result);
v3 = $result;
goto label_46;
}
ResultKt.throwOnFailure($result);
invokeResult = $result;
goto label_40;
}
//(八)返回结果
return resultOne + resultTwo + (((String) resultThree));
}
}
如果你逻辑还是很乱证明我没讲好。看一下图吧
华丽的分割线
上面的例子总是在说什么返回挂起标识COROUTINE_SUSPENDED
,但是我们怎么返回这个变量?并且状态机传入Continuation
对象我们怎么在挂起函数拿到?
挂起函数如何拿到Continuation
?
还没有评论,来说两句吧...