今天就来分析所谓的协程状态机

首先来看一个问题,我们知道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
                }
            }
        }
    }
}

综上所述我么总结一下流程:

  1. 步骤一:我们创建一个Continuation对象用户接受挂起函数完成的通知。本案例为MyCoroutine
  2. 步骤二:创建一个suspend lambda 。本案例为mystartSuspend
  3. 步骤三:mystartSuspend 内部调用其他挂起函数
  4. 步骤四:createCoroutineUnintercepted 创建一个新的Continuation 这个Continuation 对象是步骤二的suspend lambda所生成的(编译器会生成一个SuspendLambda对象)
  5. 步骤五:调用suspend lambdacontinuationonresume.
  6. 步骤六:suspend lambdacontinuation的父类BaseContinuationImpl内部resumeWith被回调
  7. 步骤七:父类BaseContinuationImpl内部resumeWith 内部调用子类invokeSuspend并获取结果
  8. 步骤八:子类invokeSuspend调用内部的挂起函数,并把自己作为continuation传入
  9. 步骤九:获取invokeSuspend返回结果。如果返回不是COROUTINE_SUSPENDED,协程结束回调监听完成的continuation类(本案例中MyCoroutine被回调)。如果返回COROUTINE_SUSPENDED那么结束父类BaseContinuationImpl内部resumeWith的死循环,并下一次等候回调resumeWith(因为子类的invokeSuspend中调用挂起函数传入的自己(continuation)给挂起函数,挂起函数完成时回调他的resumeWith方法)
  10. 步骤十:重复步骤八和九。直到返回结果

我们这里回过头来看看编译器帮我生成类(请看注释)


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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐