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.就能拿到挂起函数执行的结果了。并赋值打印。
|