Kotlin协程源码分析-3 调用挂起函数

约定不等于承诺〃 2023-07-17 14:28 140阅读 0赞

上文链接 Kotlin协程源码分析-2 调用挂起函数

继续上文从编译后的字节码继续分析,继续查看上篇文章的源码

  1. //com.example.studycoroutine.chapter.two.CoroutineRun.kt
  2. fun testOne(){
  3. val myCoroutineFun: suspend () -> String = {
  4. logD("返回 hello结果")
  5. mySuspendFun()
  6. }
  7. val myCoroutine = MyCoroutine()
  8. //这种写法是下面的封装而已
  9. //myCoroutineFun.startCoroutine(myCoroutine)
  10. val createCoroutine = myCoroutineFun.createCoroutine(myCoroutine)
  11. createCoroutine.resume(Unit)
  12. }

createCoroutine函数位于kotlin.coroutines.Continuation.kt查看对应声明

  1. public fun <T> (suspend () -> T).createCoroutine(
  2. completion: Continuation<T>
  3. ): Continuation<Unit> =
  4. SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)

SafeContinuation是一个实现了Continuation的类,主要用于代理createCoroutineUnintercepted返回Continuation。的这里我们不需要理会它,你可以简单理解上述代码等价于

  1. public fun <T> (suspend () -> T).createCoroutine(
  2. completion: Continuation<T>
  3. ): Continuation<Unit> =createCoroutineUnintercepted(completion)

createCoroutineUnintercepted声明

  1. //kotlin.coroutines.intrinsics.CoroutinesIntrinsicsH.kt
  2. @SinceKotlin("1.3")
  3. public expect fun <T> (suspend () -> T).createCoroutineUnintercepted(
  4. completion: Continuation<T>
  5. ): Continuation<Unit>

可以看到是一个跨平台类,实现类如下:

  1. //kotlin.coroutines.intrinsics.IntrinsicsKt.class
  2. @SinceKotlin("1.3")
  3. public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
  4. completion: Continuation<T>
  5. ): Continuation<Unit> {
  6. //probeCompletion 还是我们传入completion对象,在我们的Demo就是myCoroutine
  7. val probeCompletion = probeCoroutineCreated(completion)
  8. //This就是这个suspend lambda。在Demo中就是myCoroutineFun。疑问我们的lambda为什么是这个BaseContinuationImpl对象
  9. return if (this is BaseContinuationImpl)
  10. create(probeCompletion)
  11. else
  12. //else分支在我们demo中不会走到
  13. createCoroutineFromSuspendFunction(probeCompletion) {
  14. (this as Function1<Continuation<T>, Any?>).invoke(it)
  15. }
  16. }

重点代码:
(this is BaseContinuationImpl)
我们的thismyCoroutineFun对象。为什么这里会可以判断为BaseContinuationImpl对象?

  1. val myCoroutineFun: suspend () -> String = {
  2. logD("返回 hello结果")
  3. mySuspendFun()
  4. }

这这里就是编译器的黑魔法体现的地方。
我们的testOne函数所在类com.example.studycoroutine.chapter.two.CoroutineRun.kt反编译看看吧。

原始代码:

  1. package com.example.studycoroutine.chapter.two
  2. suspend fun suspendFunHaveOneParameter(msg: String): String {
  3. return "incoming parameter [$msg]";
  4. }
  5. suspend fun mySuspendFun(): String {
  6. return "hello";
  7. }
  8. fun testOne(){
  9. val myCoroutineFun: suspend () -> String = {
  10. logD("返回 hello结果")
  11. mySuspendFun()
  12. }
  13. val myCoroutine = MyCoroutine()
  14. //这种写法是下面的封装而已
  15. //myCoroutineFun.startCoroutine(myCoroutine)
  16. val createCoroutine = myCoroutineFun.createCoroutineUnintercepted(myCoroutine)
  17. createCoroutine.resume(Unit)
  18. }

反编译后包结构图:
在这里插入图片描述
我们我知道lambda表达式会在编译后转化一个对应类,myCoroutineFun将转化为上诉的图中
myCorotineFun$1

  1. val myCoroutineFun: suspend () -> String = {
  2. logD("返回 hello结果")
  3. mySuspendFun()
  4. }

来看看编译后的样子

  1. //CoroutineRunKt$testOne$myCorotineFun$1.java
  2. static final class CoroutineRunKt.testOne.myCoroutineFun.1 extends SuspendLambda implements Function1 {
  3. int label;
  4. CoroutineRunKt.testOne.myCoroutineFun.1(Continuation arg2) {
  5. super(1, arg2);
  6. }
  7. //创建对象实例传入的参数实例就是我们的myCoroutine
  8. public final Continuation create(Continuation arg2) {
  9. Intrinsics.checkParameterIsNotNull(((Object)arg2), "completion");
  10. return new CoroutineRunKt.testOne.myCoroutineFun.1(arg2);
  11. }
  12. public final Object invoke(Object arg2) {
  13. return ((CoroutineRunKt.testOne.myCoroutineFun.1)this.create(((Continuation)arg2))).invokeSuspend(Unit.INSTANCE);
  14. }
  15. //状态机本文先不做分析。后续文章重点分析所谓的状态迁移
  16. public final Object invokeSuspend(Object $result) {
  17. Object v0 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
  18. int v1 = this.label;
  19. if(v1 == 0) {
  20. ResultKt.throwOnFailure($result);
  21. LogKt.logD("返回 hello结果");
  22. this.label = 1;
  23. v1_1 = CoroutineRunKt.mySuspendFun(((Continuation)this));
  24. if(v1_1 == v0) {
  25. return v0;
  26. }
  27. }
  28. else {
  29. if(v1 == 1) {
  30. ResultKt.throwOnFailure($result);
  31. return $result;
  32. }
  33. throw new IllegalStateException("call to \'resume\' before \'invoke\' with coroutine");
  34. }
  35. return v1_1;
  36. }
  37. }

编译后的lambda继承自SuspendLambda,而SuspendLambda继承链为:
ContinuationImpl->BaseContinuationImpl->BaseContinuationImpl

在这里插入图片描述

在这里一切都释然了Continuation的创建和suspend函数的调用关系。

再回过头看看下面的代码也就没什么了

  1. //kotlin.coroutines.intrinsics.IntrinsicsKt.class
  2. @SinceKotlin("1.3")
  3. public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
  4. completion: Continuation<T>
  5. ): Continuation<Unit> {
  6. //probeCompletion 还是我们传入completion对象,在我们的Demo就是myCoroutine
  7. val probeCompletion = probeCoroutineCreated(completion)
  8. //This就是这个suspend lambda。在Demo中就是myCoroutineFun。疑问我们的lambda为什么是这个BaseContinuationImpl对象
  9. return if (this is BaseContinuationImpl)
  10. create(probeCompletion)
  11. else
  12. //else分支在我们demo中不会走到
  13. createCoroutineFromSuspendFunction(probeCompletion) {
  14. (this as Function1<Continuation<T>, Any?>).invoke(it)
  15. }
  16. }

发表评论

表情:
评论列表 (有 0 条评论,140人围观)

还没有评论,来说两句吧...

相关阅读

    相关 3.-函数

    上一篇,我们知道了非阻塞挂起的核心是要执行一个挂起函数,挂起函数的作用就是启动线程执行耗时任务,耗时任务执行完毕,通知调用线程继续执行后续的代码。那么我们如何定义挂起函数呢?有