创建协程
首先来创建一个简单的协程,使用挂起修饰符suspend
修饰一个lambda函数
val lambda: suspend () -> Int = suspend {
println("Im coroutine")
5
}
对象经编译器编译之后创建的是一个<文件名>Kt$<方法名>$continuation$1
的匿名内部类的对象,查看字节码该内部类继承自kotlin.coroutines.jvm.internal.SuspendLambda
,实现了kotlin.jvm.functions.Function1
。
SuspendLambda
的继承关系为:SuspendLambda->ContinuationImpl->BaseContinuationImpl->Continuation
标准库中提供了一个createCoroutine
函数,可以通过它来创建协程,不过这个协程并不会立即执行,先来看看它的声明:
public fun (suspend () -> T).createCoroutine(
completion: Continuation
): Continuation =
SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)
createCoroutine
是对被suspend
修饰的lambda的一个扩展函数,方法返回的具体对象是SafeContinuation
,现在协程仅仅是被创建出来,需要通过这个返回值在之后触发协程的启动,具体的使用为:
val continuation = lambda.createCoroutine(object : Continuation {
override val context: CoroutineContext = EmptyCoroutineContext
override fun resumeWith(result: Result) {
println("coroutine End: $result")
}
})
参数completion
会在协程执行完成后调用,实际上就是协程的完成回调。
SafeContinuation
在Kotlin里的声明是平台相关的,具体的实现在平台模块中实现,文件为SafeContinuationJvm
。查看SafeContinuation
的源码可知其真是身份是个代理类,具体的逻辑在其成员变量delegate
中,delegate
是在其构造函数中传入的,也就是上面的createCoroutineUnintercepted(completion).intercepted()
方法。
createCoroutineUnintercepted
方法的声明也是平台相关的,具体的实现在IntrinsicsJvm
文件中:
public actual fun (suspend () -> T).createCoroutineUnintercepted(
completion: Continuation
): Continuation {
val probeCompletion = probeCoroutineCreated(completion)
return if (this is BaseContinuationImpl)
create(probeCompletion)
else
createCoroutineFromSuspendFunction(probeCompletion) {
(this as Function1, Any?>).invoke(it)
}
}
probeCoroutineCreated
方法返回的就是completion自身,根据上面的继承关系可知lambda继承自BaseContinuationImpl
,会调用BaseContinuationImpl
的create方法,传入completion,create方法在编译器根据lambda自动生成的文件中进行了覆写,查看lambda的字节码文件声明如下:
@NotNull
public final Continuation create(@NotNull Continuation completion) {
Intrinsics.checkParameterIsNotNull(completion, "completion");
Function1 var2 = new (completion);
return var2;
}
方法里再次调用了lambda生成的匿名内部类的构造函数,并在构造函数中将completion传入,根据继承关系调用父类构造函数,completion最后会传到BaseContinuationImpl
的构造函数并被保存下来。
最后看一下intercepted()
方法,和createCoroutineUnintercepted
一样,具体的实现在IntrinsicsJvm
文件中:
public actual fun Continuation.intercepted(): Continuation =
(this as? ContinuationImpl)?.intercepted() ?: this
根据继承关系,匿名内部类是ContinuationImpl
的子类,调用了ContinuationImpl
的intercepted()
方法:
@Transient
private var intercepted: Continuation? = null
public fun intercepted(): Continuation =
intercepted
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
.also { intercepted = it }
在之前的构造方法中没有传入CoroutineContext
,此处返回值就是自身。
所以createCoroutineUnintercepted(completion).intercepted()
方法返回的就是一个编译器根据lambda生成的匿名内部类的对象,到此一个简单协程的创建流程就梳理完了。
启动协程
调用continuation.resume(Unit)
之后,协程体会立即开始执行。resume
方法的实现为:
public inline fun Continuation.resume(value: T): Unit =
resumeWith(Result.success(value))
此处调用了SafeContinuation
的resumeWith
方法:
public actual override fun resumeWith(result: Result) {
while (true) { // lock-free loop
val cur = this.result // atomic read
when {
cur === UNDECIDED -> if (RESULT.compareAndSet(this, UNDECIDED, result.value)) return
cur === COROUTINE_SUSPENDED -> if (RESULT.compareAndSet(this, COROUTINE_SUSPENDED, RESUMED)) {
delegate.resumeWith(result)
return
}
else -> throw IllegalStateException("Already resumed")
}
}
}
result
的值为createCoroutine
方法中调用SafeContinuation
构造函数时传入的COROUTINE_SUSPENDED
,所以when方法会走到第二个分支,会调用delegate.resumeWith(result)
,delegate
的对象为BaseContinuationImpl
的子类,会调用BaseContinuationImpl
的resumeWith
方法:
public final override fun resumeWith(result: Result) {
// 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 =
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
}
}
}
}
result
为调用resume
方法时传入的Unit
,probeCoroutineResumed
为空实现,invokeSuspend
为BaseContinuationImpl
内的抽象方法,编译器根据lambda生成的匿名内部类中对该方法进行了实现,具体实现查看字节码:
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
return Boxing.boxInt(5);
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
}
因为invokeSuspend
方法的返回值是Object的,lambda中只返回了一个数字5,此处会对数字进行一下装箱然后进行返回。
resumeWith
方法中outcome
的值为Integer类型的5,completion
为之前createCoroutine
传入的Continuation
实现类对象,其父类不是BaseContinuationImpl
,此时会调用completion
的resumeWith
方法,最后打印出结果为:
coroutine End: Result.Success(5)
至此简单协程的创建与启动就分析完成了。