kotlin协程原理

1 篇文章 0 订阅
1 篇文章 0 订阅

kotlin的协程包括基础设施部分和在基础设施上封装的库。主要看下自己如何使用基础设施部分以及它的原理。

首先看下我们如何使用的。

1.创建协程。

fun <T> launch(block : suspend () -> T){
    val continuation = block.createCoroutine(object : Continuation<T> {
        override val context: CoroutineContext = EmptyCoroutineContext

        override fun resumeWith(result: Result<T>) {
            println(result.getOrNull().toString())
        }
    })
    continuation.resume(Unit)
}

传入一个挂起函数通过createCoroutine方法传入一个Continuation对象创建协程。

然后是我们的挂起函数。

suspend fun loadData() = suspendCoroutine<String> {
    it.resume("abcd")
}

我们平时写挂起函数的时候并不这么写,这么写主要是因为我们要拿到传入的Continuation对象,就是it.

然后就是使用。

fun main(){
    launch {
        val loadImg = loadData()
        println("img = $loadImg")
    }
}

代理本身没有什么意义。我们将这些代码反编译一下。首先是launch部分。

public static final void launch(@NotNull Function1 block) {
      Intrinsics.checkNotNullParameter(block, "block");
      Continuation coroutine = ContinuationKt.createCoroutine(block, (Continuation)(new Continuation() {
         @NotNull
         private final CoroutineContext context;

         @NotNull
         public CoroutineContext getContext() {
            return this.context;
         }

         public void resumeWith(@NotNull Object result) {
            boolean var3 = false;
            String var2 = String.valueOf(Result.isFailure-impl(result) ? null : result);
            var3 = false;
            System.out.println(var2);
         }

         {
            this.context = (CoroutineContext)EmptyCoroutineContext.INSTANCE;
         }
      }));
      Unit var3 = Unit.INSTANCE;
      boolean var4 = false;
      Companion var5 = Result.Companion;
      boolean var6 = false;
      coroutine.resumeWith(Result.constructor-impl(var3));
   }

然后是挂起函数部分

public static final Object loadImg(@NotNull Continuation $completion) {
      boolean var1 = false;
      boolean var2 = false;
      boolean var3 = false;
      SafeContinuation var4 = new SafeContinuation(IntrinsicsKt.intercepted($completion));
      Continuation it = (Continuation)var4;
      int var6 = false;
      String var8 = "abcd";
      boolean var9 = false;
      Companion var10 = Result.Companion;
      boolean var11 = false;
      it.resumeWith(Result.constructor-impl(var8));
      Object var10000 = var4.getOrThrow();
      if (var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
         DebugProbesKt.probeCoroutineSuspended($completion);
      }

      return var10000;
   }

最后是我们使用的。

public static final void main() {
      launch((Function1)(new Function1((Continuation)null) {
         int label;

         @Nullable
         public final Object invokeSuspend(@NotNull Object $result) {
            Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
            Object var10000;
            switch(this.label) {
            case 0:
               ResultKt.throwOnFailure($result);
               this.label = 1;
               var10000 = MainKt.loadImg(this);
               if (var10000 == var5) {
                  return var5;
               }
               break;
            case 1:
               ResultKt.throwOnFailure($result);
               var10000 = $result;
               break;
            default:
               throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
            }

            String loadImg = (String)var10000;
            String var3 = "img = " + loadImg;
            boolean var4 = false;
            System.out.println(var3);
            return Unit.INSTANCE;
         }

         @NotNull
         public final Continuation create(@NotNull Continuation completion) {
            Intrinsics.checkNotNullParameter(completion, "completion");
            Function1 var2 = new <anonymous constructor>(completion);
            return var2;
         }

         public final Object invoke(Object var1) {
            return ((<undefinedtype>)this.create((Continuation)var1)).invokeSuspend(Unit.INSTANCE);
         }
      }));
   }

这里面看着有点奇怪,不明白怎么反编译成这个样子。其实,launch传入的block会被编译成一个类。这个类继承自SuspendLambda实现了Function1接口。我们简单看下。

internal abstract class SuspendLambda(
    public override val arity: Int,
    completion: Continuation<Any?>?
) : ContinuationImpl(completion), FunctionBase<Any?>, SuspendFunction {
    constructor(arity: Int) : this(arity, null)

    public override fun toString(): String =
        if (completion == null)
            Reflection.renderLambdaToString(this) // this is lambda
        else
            super.toString() // this is continuation
}

internal abstract class ContinuationImpl(
    completion: Continuation<Any?>?,
    private val _context: CoroutineContext?
) : BaseContinuationImpl(completion) {...}

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
            probeCoroutineResumed(current)
            with(current) {
                val completion = completion!! // fail fast when trying to resume continuation without completion
                val outcome: Result<Any?> =
                    try {
                        val outcome = invokeSuspend(param)
                        if (outcome === COROUTINE_SUSPENDED) return
                        Result.success(outcome)
                    } catch (exception: Throwable) {
                        Result.failure(exception)
                    }
                releaseIntercepted() // this state machine instance is terminating
                if (completion is BaseContinuationImpl) {
                    // unrolling recursion via loop
                    current = completion
                    param = outcome
                } else {
                    // top-level completion reached -- invoke and return
                    completion.resumeWith(outcome)
                    return
                }
            }
        }
    }

    protected abstract fun invokeSuspend(result: Result<Any?>): Any?

    protected open fun releaseIntercepted() {
        // does nothing here, overridden in ContinuationImpl
    }

    public open fun create(completion: Continuation<*>): Continuation<Unit> {
        throw UnsupportedOperationException("create(Continuation) has not been overridden")
    }

    public open fun create(value: Any?, completion: Continuation<*>): Continuation<Unit> {
        throw UnsupportedOperationException("create(Any?;Continuation) has not been overridden")
    }

    public override fun toString(): String =
        "Continuation at ${getStackTraceElement() ?: this::class.java.name}"

    // --- CoroutineStackFrame implementation

    public override val callerFrame: CoroutineStackFrame?
        get() = completion as? CoroutineStackFrame

    public override fun getStackTraceElement(): StackTraceElement? =
        getStackTraceElementImpl()
}

SuspendLambda又继承了ContinuationImpl抽象类,ContinuationImpl又继承自BaseContinuationImpl类。我们先留意下BaseContinuationImpl中的resumeWith方法并且又invokeSuspend的抽象方法。再回头看下生成的类是不是就实现了invokeSuspend方法。

可以看到当创建协程的时候调用了ContinuationKt.createCoroutine方法。

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

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)
        }
}

可以看到createCoroutine调用到了createCoroutineUnintercepted方法。在该方法中判断了this is BaseContinuationImpl.因为这两个方法本就是suspend () -> T的扩展方法。

那么现在传入到launch中的就是生成的继承自SuspendLambda的那个类的对象。上面我们看了该类就是间接的继承自BaseContinuationImpl,因此回调用到该类的create方法。该类的create方法做了什么呢就是将我们创建的匿名的Continuation对象传入生成一个新的对象并返回。

然后当使用返回的Continuation对象调用其resume方法的时候,其实是调用到了它的resumeWith方法。如下。

public inline fun <T> Continuation<T>.resume(value: T): Unit =
    resumeWith(Result.success(value))

resumeWith方法在哪儿呢,前面提到了,就在BaseContinuationImpl里面。会调用到重写的invokeSuspend方法。第一次进入的时候lable为0,走第一个case,lable赋值为1,调用到loadImg方法,loadImg这个挂起函数真正挂起的话getOrThrow方法会立刻返回COROUTINE_SUSPENDED,标记挂起了。var5也就是COROUTINE_SUSPENDED,然后就会返回到BaseContinuationImpl中的resumeWith中。if (outcome === COROUTINE_SUSPENDED) return就跳出循环。

然后如果过了一段时间挂起函数执行完了。就会调用Continuation对象(这个其实就是生成的继承自Suspendlambda的类)的resume或者resumeWith方法,最终还是调用到BaseContinuationImpl的resumeWith,并将结果传进去,然后是生成的类的invokeSuspend方法。现在lable已经变成1了,就会走case 1.就能拿到挂起函数执行的结果了。并赋值打印。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值