时间:2022-10-14 10:24:43 | 栏目:Android代码 | 点击:次
协程可以简化异步编程,可以顺序地表达程序,协程也提供了一种避免阻塞线程并用更廉价、更可控的操作替代线程阻塞的方法 – 挂起函数。
Kotlin 的协程是依靠编译器实现的, 并不需要操作系统和硬件的支持。编译器为了让开发者编写代码更简单方便, 提供了一些关键字(例如suspend), 并在内部自动生成了一些支持型的代码。
fun create.main() { //1. 创建协程体 val coroutine = suspend { println("in coroutine") 5 }.createCoroutine(object: Continuation<Int> { override fun resumeWith(result: Result<Int>) { println("coroutine end: $result") } override val context: CoroutineContext get() = EmptyCoroutineContext }) //2. 执行协程 coroutine.resume(Unit) }
上面代码的输出结果:
in coroutine coroutine end: Success(5)
调用栈流程如下
我们分析调用栈得知,resumeWith 最终是在 BaseContinuationImpl 中执行的,下面来看看代码
@SinceKotlin("1.3") 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) { probeCoroutineResumed(current) with(current) { val completion = completion!! val outcome: Result<Any?> = try { val outcome = invokeSuspend(param) //1.这里执行了 suspend block 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) //2.这里回调了我们的匿名内部类 return } } } } protected abstract fun invokeSuspend(result: Result<Any?>): Any? //3. 抽象方法 }
在代码注释 1. 处,调用 current.invokeSuspend,执行了我们定义的协程体,证明 suspend block 其实是 BaseContinuationImpl 的子类
在 2. 处,协程体执行完毕后,我们的代码收到了完成回调
在 3. 处,可以发现 invokeSuspend 是个抽象方法,suspend block 就是这个方法的具体实现
下面我通过断点,进一步分析 suspend block 是通过哪个子类执行的。
可以看到 current 是名为 {文件}${方法}${变量}$1 格式的对象,证明 kotlin 编译器遇到 suspend 关键字后会帮我们生成一个 BaseContinuationImpl 的子类
那么,这个子类到底是什么呢?将 kt 编译为 .class 再通过 jadx 打开后,得到的 java 代码如下
public final class CreateCoroutineKt { public static final void create.main() { Continuation coroutine = ContinuationKt.createCoroutine(new CreateCoroutineKt$create.main$coroutine$1(null), new CreateCoroutineKt$create.main$coroutine$2()); Unit unit = Unit.INSTANCE; Result.Companion companion = Result.Companion; coroutine.resumeWith(Result.constructor-impl(unit)); } }
final class CreateCoroutineKt$create.main$coroutine$1 extends SuspendLambda implements Function1<Continuation<? super Integer>, Object> { int label; CreateCoroutineKt$create.main$coroutine$1(Continuation<? super CreateCoroutineKt$create.main$coroutine$1> continuation) { super(1, continuation); } @NotNull public final Continuation<Unit> create(@NotNull Continuation<?> continuation) { return new CreateCoroutineKt$create.main$coroutine$1(continuation); } @Nullable public final Object invoke(@Nullable Continuation<? super Integer> continuation) { return create(continuation).invokeSuspend(Unit.INSTANCE); } @Nullable public final Object invokeSuspend(@NotNull Object obj) { IntrinsicsKt.getCOROUTINE_SUSPENDED(); switch (this.label) { case 0: ResultKt.throwOnFailure(obj); System.out.println((Object) "in coroutine"); //协程体的逻辑 return Boxing.boxInt(5); default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } } }
明显看出,kt 编译器帮助我们把 suspend 关键字变为了 SuspendLambda 的 子类,并重写了 invokeSuspend 方法,不难猜出 SuspendLambda 继承自 BaseContinuationImp
用一个类图简单的总结一个协程创建并执行的过程。
suspend block(lambda) 在编译时会转变为 SuspendLambda 的匿名子类,并把 block 中的逻辑通过重写 invokeSuspend 实现
调用 suspend_lambda.createCoroutine 会得到 SafeContinuation 对象,这只是一个代理类,代理的对象正是我们传入的 SuspendLambda
createCoroutine 的参数是 completion,代表协程执行完毕的回调
最终调用到了 BaseContinuationImpl 的 resumeWith,完成协程的调用,调用完毕的回调