Kotlin协程系列文章:
《二、Kotlin协程系列:协程的线程调度》
本文是分析Kotlin协程系列文章的第一篇,在本篇中分为2大块分析协程创建和启动流程;挂起和恢复流程。其中在分析协程的创建启动流程中,我们将解答:
kotlin编译器是如何处理
suspend Lambda
的,或者说处理
suspend
关键字的?
传入协程
lambda
的代码是如何被执行到的?
在分析协程的挂起和恢复流程中,我们将解答:
协程挂起的本质是什么?又是如何被挂起的?挂起会阻塞执行线程吗?
协程是如何做到用同步调用的方式执行异步代码?
协程在挂起后,又是如何恢复执行的?
一、协程的创建与启动
一般来说开启协程有2种方式:
CoroutineScope.launch()
CoroutineScope.async()
我们以第一种来做为主流程分析,其他方式殊途同归。
1.1 suspend Lambda表达式的编译处理
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GlobalScope.launch {
Log.d(TAG, "onCreate: print in launch.")
当调用GloubalScope.launch()方法开启一个协程时,调用的是GlobalScope
的扩展函数,该函数原型如下:
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
观察函数签名原型可以知道,在启动协程时传入的lambda表达式是一个suspend CoroutineScope.() -> Unit
,而Kotlin编译器其实会将传入的协程体进行处理,生成一个继承于SuspendLambda的实体类。这块就必须要反编译才能看到了,详见1.1.1流程。
1.1.1 suspend Lambda的编译产物
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Job unused = BuildersKt__Builders_commonKt
.launch$default(GlobalScope.INSTANCE,
null, null, new MainActivity$onCreate$1(null), 3, null);
final class MainActivity$onCreate$1 extends SuspendLambda implements Function2<CoroutineScope, Continuation<? super Unit>, Object> {
int label;
public MainActivity$onCreate$1(Continuation<? super MainActivity$onCreate$1> continuation) {
super(2, continuation);
@Override
public final Continuation<Unit> create(Object obj, Continuation<?> continuation) {
return new MainActivity$onCreate$1(continuation);
@Override
public final Object invokeSuspend(Object obj) {
IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (this.label) {
case 0:
ResultKt.throwOnFailure(obj);
Log.d(MainActivity.TAG, LiveLiterals$MainActivityKt.INSTANCE.m4146xf96cab04());
return Unit.INSTANCE;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
这里可以看到在源码中传入的block参数
在这里被生成一个MainActivity$onCreate$1
类型的实例,该类型实例有如下特点:
继承于SuspendLambda
。
实现了BaseContinuationImpl.create()
方法,该方法用于创建自身实例。
实现了BaseContinuationImpl.invokeSuspend()
方法,该方法包含了真正的协程逻辑代码,并且用状态机的方式实现了协程的挂起/恢复逻辑。
为了探究自动生成的实例类型究竟是何物,我们继续沿着它继承的SuspendLambda
往上看。
1.1.2 SuspendLambda
@SinceKotlin("1.3")
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)
super.toString()
可以看到SuspendLambda
继承至ContinuationImpl
:
SuspendLambda
其实没有扩展什么实际功能,只扩展了个无关紧要的toString()
方法。
注意这里将conmpletion
成员变量继续传给ContinuationImpl
父类。
接下来沿着ContinuationImpl
一步步往上探究续体。
1.1.3 ContinuationImpl
internal abstract class ContinuationImpl(
completion: Continuation<Any?>?,
private val _context: CoroutineContext?
) : BaseContinuationImpl(completion) {
constructor(completion: Continuation<Any?>?)
: this(completion, completion?.context)
public override val context: CoroutineContext
get() = _context!!
@Transient
private var intercepted: Continuation<Any?>? = null
public fun intercepted(): Continuation<Any?> =
intercepted
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
.also { intercepted = it }
protected override fun releaseIntercepted() {
ContinuationImpl
继承至BaseContinuationImpl
:
注意这里,ContinuationImpl
所使用的协程上下文其实用的是completion.context
。而将completion
续体继续往父类传递。
ContinuationImpl
主要是扩展增加了intercepted()
,这对于后续协程的线程调度起到关键作用。
1.1.4 BaseContinuationImpl
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) {
with(current) {
val completion = 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()
if (completion is BaseContinuationImpl) {
current = completion
param = outcome
} else {
completion.resumeWith(outcome)
return
protected abstract fun invokeSuspend(result: Result<Any?>): Any?
protected open fun releaseIntercepted() {
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")
1.1.5 总结
我们通过反编译搞清楚了suspend CoroutineScope.() -> Unit
函数类型其实在编译器的处理过后,会生成一个继承于SuspendLambda
类型的类,并且传入CoroutineScope.launch()
方法的也是这个实例的类型。
自动生成的实例实现了create()
方法和invokeSuspend()
方法。
SuspendLambda
继承于ContinuationImpl
类型,实现了intercepted()
方法,用于取出当前协程上下文中的调度器。而SuspendLambda
的协程上下文使用的是completion
的上下文。completion
续体是1.2.1小节中启动时,创建的一个StandaloneCoroutine
续体。
ContinuationImpl
继承于BaseContinuationImpl
类型,该类型有关键的resumeWith()
方法和completion: Continuation<Any?>?
成员变量。
1.2 协程的启动流程
上一小节中,预先分析了suspend lambda
在被Kotlion编译器处理后的代码,本小节接着分析协程的启动流程。
1.2.1 CoroutineScope.launch()
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
一般的,我们使用CoroutineScope.launch()
启动一个协程,步进此方法源码有如下几个要点:
复制Context,并默认使用了Dispatchers.Default
创建了一个新的协程体StandaloneCoroutine
,后问我们在本文中称之为协程体1,注意这个StandaloneCoroutine
继承Continuation<T>
,所以它其实也是个续体。
调用StandaloneCoroutine.start()
方法,注意这里第二个参数又有把自己传入当作参数。详见1.2.2。
1.2.2 StandaloneCoroutine.start()
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
start(block, receiver, this)
@InternalCoroutinesApi
public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit =
when (this) {
DEFAULT -> block.startCoroutineCancellable(receiver, completion)
ATOMIC -> block.startCoroutine(receiver, completion)
UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
LAZY -> Unit
这里其实是由于Kotlin的语法糖,所以实际调用了CoroutineStart.invoke()
方法,由于在上面中,我们用默认的CoroutineStart.DEFAULT
。注意这里的receiver
等于之前新建的coroutine
,而这里的this也是coroutine
。
可以看到其实调用的是block
的方法。
1.2.3 真正启动协程 startCoroutineCancellable()
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
receiver: R, completion: Continuation<T>,
onCancellation: ((cause: Throwable) -> Unit)? = null
runSafely(completion) {
createCoroutineUnintercepted(receiver, completion)
.intercepted()
.resumeCancellableWith(Result.success(Unit), onCancellation)
可以看到,startCoroutineCancellable
其实是(suspend (R) -> T)
类型的一个扩展函数。这里三个调用步步关键,但是在这个小节中主要探讨协程的创建,所以我们只关心1流程。
createCoroutineUnintercepted()
方法追踪进去会到IntrinsicsKt.class
类,在这里看不到具体实现,因为这是平台相关性,所以需要直接定位到IntrinsicsJvm.class
就能找到该方法在JVM虚拟机上的实现。
1.2.4 createCoroutineUnintercepted()
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
receiver: R,
completion: Continuation<T>
): Continuation<Unit> {
val probeCompletion = probeCoroutineCreated(completion)
return if (this is BaseContinuationImpl)
create(receiver, probeCompletion)
else {
createCoroutineFromSuspendFunction(probeCompletion) {
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, it)
由之前对Suspend Lambda
的编译分析可知,(suspend () -> T)
实际生成的是实例是继承BaseContinuationImpl
,所以1调用步骤。而1调用了BaseContinuationImpl.create()
方法,此方法在生成的SuspendLambda实例被实现,最终调用到 1.1.1#1流程 具体往下看1.2.4.1小节。
1.2.4.1 create()
@Override
public final Continuation<Unit> create(Object obj, Continuation<?> continuation) {
return new MainActivity$onCreate$1(continuation);
可以看到这个方法里就直接new了一个MainActivity$onCreate$1
类型实例,具体可以参考1.1小节。后续我们称此为续体1。
这里需要注意到的是,构建是传入了一个continuation
变量,这个变量最终赋值给了BaseContinuationImpl.completion
成员变量,这里可以参考1.1.4小节。而这个continuation
变量就是在1.2.1小节中创建的一个StandaloneCoroutine
实例,协程体1。
1.2.4 intercepted()
public fun intercepted(): Continuation<Any?> =
intercepted
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
.also { intercepted = it }
此方法是取出协程是下午文中的interceptor
,用于协程调度。为了简化例子,在这个例子中使用的是Dispatcher.Default
,而interceptContinuation
会创建一个DispatchedContinuation
对象。
1.2.5 resumeCancellableWith
@InternalCoroutinesApi
public fun <T> Continuation<T>.resumeCancellableWith(
result: Result<T>,
onCancellation: ((cause: Throwable) -> Unit)? = null
): Unit = when (this) {
is DispatchedContinuation -> resumeCancellableWith(result, onCancellation)
else -> resumeWith(result)
此时续体已经被包装成一个DispatchedContinuation
对象,但是本小节为了简化流程,所以看作是调用了下面else分支。这块协程线程调度的逻辑将在第二篇文章分析。
可以看到最终他是调用到了resumeWith()
,而此方法就是前文1.1.4小节提到状态机启动的入口。流程上可以看下面1.2.5.1小节。
1.2.5.1 resumeWith()
public final override fun resumeWith(result: Result<Any?>) {
while (true) {
with(current) {
val outcome: Result<Any?> =
val outcome = invokeSuspend(param)
简化了代码,只保留了主流程上的关键代码。
此方法是续体1的父类的默认实现。
可以看到调用了invokeSuspend()
方法,此方法是由编译器在生成续体1时,自动生成实现的方法。详见1.2.5.2。
1.2.5.2 invokeSuspend()
public final Object invokeSuspend(Object obj) {
IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (this.label) {
case 0:
ResultKt.throwOnFailure(obj);
Log.d(MainActivity.TAG, "onCreate: print in launch.");
return Unit.INSTANCE;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
在自动生成的方法体里,有我们写的协程内容代码,所以调用此方法便会触发我们希望协程执行的逻辑代码。
1.2.5 总结
通过launch启动协程时,会先创建一个StandaloneCoroutine
续体作为结束回调,最终传给生成的SuspendLambda
实例作为其成员变量。
createCoroutineUnintercepted()
方法的实现其实是在IntrinsicsJvm.class
文件里,他调用了生成的实例的create()
方法,最终new出了一个MainActivity$onCreate$1
类型实例。
协程代码的执行触发流程:BaseContinuationImpl.resumeWith()
->BaseContinuationImpl.resume()
->MainActivity$onCreate$1.invokeSuspend()
二、协程挂起/恢复
在前面小节中,我们用一个简单的协程例子,剖析了协程启动过程的中细节,其中要点包括:
SuspendLambda的自动生成。
新建外层续体作为completion
回调。
触发协程业务逻辑代码的流程分析。
在本小节中,我们将要探讨的是协程的挂起/恢复机制。协程的挂起/恢复机制是协程的一大特点,它使得本来异步编程可以像同步编程方式一样直观明了,而不用陷入层层回调中。为了能够分析此流程,我们需要稍微复杂一点的协程代码例子:
2.1 协程-挂起
class MainActivity : ComponentActivity() {
val TAG = "MainActivity"
@OptIn(DelicateCoroutinesApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GlobalScope.launch {
Log.d(TAG, "onCreate: log in continue.")
job1()
Log.d(TAG, "onCreate: ready to run job2.")
job2()
suspend fun job1() {
delay(1000)
Log.d(TAG, "job1: finish.")
suspend fun job2() {
Log.d(TAG, "job2: finish.")
在开启本小节的分析前,先简单扼要的阐述下协程神秘的挂起/恢复机制为什么可以化异步为同步:
在协程内,当执行到一个job1 挂起函数时,会将外层的续体Continuation
作为参数传入,修改状态机的label标志位。
并挂起函数执行异步业务逻辑并同步的返回一个SUSPEND
标志位,此时外层协程方法会被return。
当job1函数异步流程执行结束,会调用Continuation.resume()
方法,通知外层续体继续执行,由于此时状态机的label已经被修改,所以会接着执行挂起函数后面的逻辑。
在这个例子,启动了一个协程,在这个协程里,我们分别调用了suspend 函数job1和job2。从第一小节的分析我们可知,此协程代码会被编译器进一步处理。
2.1.1 挂起:SuspendLambda 匿名内部类
protected void onCreate(Bundle paramBundle)
super.onCreate(paramBundle);
BuildersKt.launch$default((CoroutineScope)GlobalScope.INSTANCE, null, null, (Function2)new SuspendLambda(null)
int label;
public final Object invokeSuspend(Object paramObject)
Object localObject = IntrinsicsKt.getCOROUTINE_SUSPENDED();
1 local1;
switch (this.label)
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
case 2:
ResultKt.throwOnFailure(paramObject);
break;
case 1:
local1 = this;
ResultKt.throwOnFailure(paramObject);
break;
case 0:
ResultKt.throwOnFailure(paramObject);
local1 = this;
Log.d(local1.this$0.getTAG(), "onCreate: log in continue.");
MainActivity localMainActivity1 = local1.this$0;
Continuation localContinuation1 = (Continuation)local1;
local1.label = 1;
if (localMainActivity1.job1(localContinuation1) != localObject)
break;
return localObject;
Log.d(local1.this$0.getTAG(), "onCreate: ready to run job2.");
MainActivity localMainActivity2 = local1.this$0;
Continuation localContinuation2 = (Continuation)local1;
local1.label = 2;
if (localMainActivity2.job2(localContinuation2) == localObject)
return localObject;
return Unit.INSTANCE;
, 3, null);
通过分析反编译出来的协程代码得知,写在协程里面的业务代码是基于状态机来执行的,当来到要调用job1函数时:
此时label==0
,进入case 0。
将label置为1。
将续体1也就是SuspendLambda
作为参数传入job1。这里注意到在我们写的源码中job函数是没有任何入参的,此时调用却需要这个入参,说明job函数在编译时被增加了这个类型入参。
这也是suspend关键字修饰的函数为什么只能在协程中调用的原因,因为它需要一个续体作为参数。
接下来继续步进流程job1()
函数。
2.1.2 挂起:job1()
public final Object job1(Continuation<? super Unit> paramContinuation)
// 1. 将外层续体1作为参数,新建一个协程实现类。
job1.1 local11 = new ContinuationImpl(paramContinuation)
Object L$0
int label
public final Object invokeSuspend(Object paramObject)
this.result = paramObject
this.label = (0x80000000 | this.label)
//注意这里调用回job1()函数
return MainActivity.this.job1((Continuation)this)
label46: job1.1 local12 = local11
Object localObject1 = local12.result
// 记录标志位
Object localObject2 = IntrinsicsKt.getCOROUTINE_SUSPENDED()
MainActivity localMainActivity
switch (local12.label)
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine")
case 1:
localMainActivity = (MainActivity)local12.L$0
ResultKt.throwOnFailure(localObject1)
break
case 0:
ResultKt.throwOnFailure(localObject1)
long l = LiveLiterals.MainActivityKt.INSTANCE.Long$arg-0$call-delay$fun-job1$class-MainActivity()
local12.L$0 = this
//2. 更新label
local12.label = 1
//3. 调用delay函数
if (DelayKt.delay(l, local12) == localObject2)
//4. 等于SUSPEND标志位,直接返回SUSPEND标志位
return localObject2
localMainActivity = this
Log.d(localMainActivity.TAG, "job1: finish.")
return Unit.INSTANCE
在job1()函数中:
先将外层续体1作为参数生成一个续体实现类,我们称之为job1续体,并复制给local11
。
进入case0,并更新label=1。
调用delay函数,这个函数是一个延时函数,这里并不打算深究进去,但是可以猜想到的是:
在delay函数里面会开启一个定时任务并先返回一个COROUTINE_SUSPENDED
标志位。
时间到时,定时任务触发时,调用local12.resumeWith()
切换到job1续体实现类代码,
resumeWith
经过调用进入到local12.inovkeSuspen()
函数。
在流程3中,由于调用了delay函数,其返回SUPEND
标志位,判断成立,函数返回。所以流程又回到了2.1.1#4步骤:此时又在续体1中判定了一次:COROUTINE_SUSPENDED != COROUTINE_SUSPENDED
判定不成功,所以return
此inovkeSuspend()
函数。
此时便回到我们调用invokSuspend()
函数的地方,通过第一小节可以知道是BaContinuationImpl.resumtWith()
详细见 2.2。
2.1.3 挂起:job2()
public final Object job2(Continuation<? super Unit> paramContinuation)
Log.d(this.TAG, LiveLiterals.MainActivityKt.INSTANCE.String$arg-1$call-d$fun-job2$class-MainActivity());
return Unit.INSTANCE;
job2 函数虽然被suspend
修饰,但是内部并没用调用任何挂起函数,内部也就不会返回SUSPEND标志位,所以和普通函数一致。
2.2 挂起:resumeWith()
public final override fun resumeWith(result: Result<Any?>) {
var current = this
var param = result
while (true) {
with(current) {
val completion = 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()
if (completion is BaseContinuationImpl) {
current = completion
param = outcome
} else {
completion.resumeWith(outcome)
return
此时返回挂起标志位,所以会将此方法直接return掉,至此协程实现了挂起。
但是挂起后的协程是怎么被恢复的呢?这个需要接着2.1.2的delay
函数分析流程。
2.2 协程-恢复
2.2.1 恢复:job1()
public final Object job1(Continuation<? super Unit> paramContinuation)
job1.1 local11 = new ContinuationImpl(paramContinuation)
Object L$0;
int label;
public final Object invokeSuspend(Object paramObject)
this.result = paramObject;
this.label = (0x80000000 | this.label);
return MainActivity.this.job1((Continuation)this);
switch (local12.label)
case 1:
localMainActivity = (MainActivity)local12.L$0;
ResultKt.throwOnFailure(localObject1);
break;
case 0:
Log.d(localMainActivity.TAG, "job1: finish.");
return Unit.INSTANCE;
在2.1.2#3步骤分析中,我们知晓delay
函数,在定时时间到时,调用job1续体的resume()
函数通知恢复,所以会回到job1续体的invokeSuspend
方法,此时:
此时label==1,所以进入case1,随后return Unit.INSTANCE
。
根据之前的分析,此时会直接返回到local11.resumeWith()
详见2.2.2
2.2.2 恢复 Job1.resumeWith()
public final override fun resumeWith(result: Result<Any?>) {
while (true) {
with(current) {
val completion = 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()
if (completion is BaseContinuationImpl) {
current = completion
param = outcome
} else {
completion.resumeWith(outcome)
return
从2.3 可以知道返回的是Unit.INSTANCE
,所以进入到下面的if判断。
completion
就是job函数传入进来的,也就是我们的外层的协程续体1,实际为是DispatchedCoroutine
类型,调用了completion.resumeWith(outcome)
。就会进行协程的线程切换,并且最终调用到外层实际协程体的SuspendLambda.resumeWith()
->SuspendLambda.invokSuspended()
方法。
这里为什么是DispatchedCoroutine
,因为这里涉及到了协程的线程调度,所以需要调用resumeWith进行线程调度,而BaseContinuationImpl就不用,直接相当于同步代码往下执行了。
2.2.3 恢复:SuspendLambda.invokSuspended()
public final Object invokeSuspend(Object paramObject)
Object localObject = IntrinsicsKt.getCOROUTINE_SUSPENDED();
1 local1;
switch (this.label)
case 1:
local1 = this;
ResultKt.throwOnFailure(paramObject);
break;
Log.d(local1.this$0.getTAG(), LiveLiterals.MainActivityKt.INSTANCE.String$arg-1$call-d-1$fun-$anonymous$$arg-2$call-launch$fun-onCreate$class-MainActivity());
MainActivity localMainActivity2 = local1.this$0;
Continuation localContinuation2 = (Continuation)local1;
local1.label = 2;
if (localMainActivity2.job2(localContinuation2) == localObject)
return localObject;
return Unit.INSTANCE;
再次回到这个函数时:
label因为之前置为1了,所以进入case1流程
先置label=2
和调用job1时一样, 将外层续体作为参数传入job2。
自此,主协程便又恢复了运行,接着去执行job2的代码了。后续的逻辑如果由挂起就还是和job1的流程一致,如果job2里面没调用挂起函数,那么也就没有挂起标志,所以直接同步执行后结束主协程的业务逻辑。
2.3 总结
suspend 修饰的函数会经过CPS处理后,为函数增加一个Continuation
的入参,而这个入参的值便是调用此函数的父协程。
协程的挂起在于:执行到异步步耗时机制时,它提供了一个SUSPEND
标志以供返回,协程的状态机检测到这个标识位后便return
了这个函数,此为挂起。
协程的恢复在于:当耗时或者异步任务结束时,可以调用Continuation.resumeWith()
恢复协程,最终此函数的协程走完,便会调用completion.resumeWith(outcome)
,这个completion
便是之前传入的父协程。自此父协程恢复状态机的运转,接着执行后续的代码。
协程的挂起不是当前协程一直阻塞在挂起函数的执行,而是直接return掉,等后续挂起函数执行完成后在通过回调的方式通知父协程继续执行后需的代码。
三、协程中的概念说明
在上文中我们经常提到协程,续体。本小节针对这2个概念在协程源码中的代表类型进行简要的说明。
3.1 协程体
* Abstract base class for implementation of coroutines in coroutine builders.
* This class implements completion [Continuation], [Job], and [CoroutineScope] interfaces.
* It stores the result of continuation in the state of the job.
* This coroutine waits for children coroutines to finish before completing and
* fails through an intermediate _failing_ state.
* The following methods are available for override:
* * [onStart] is invoked when the coroutine was created in non-active state and is being [started][Job.start].
* * [onCancelling] is invoked as soon as the coroutine starts being cancelled for any reason (or completes).
* * [onCompleted] is invoked when the coroutine completes with a value.
* * [onCancelled] in invoked when the coroutine completes with an exception (cancelled).
* @param parentContext the context of the parent coroutine.
* @param initParentJob specifies whether the parent-child relationship should be instantiated directly
* in `AbstractCoroutine` constructor. If set to `false`, it's the responsibility of the child class
* to invoke [initParentJob] manually.
* @param active when `true` (by default), the coroutine is created in the _active_ state, otherwise it is created in the _new_ state.
* See [Job] for details.
* @suppress **This an internal API and should not be used from general code.**
@InternalCoroutinesApi
public abstract class AbstractCoroutine<in T>(
parentContext: CoroutineContext,
initParentJob: Boolean,
active: Boolean
) : JobSupport(active), Job, Continuation<T>, CoroutineScope {
AbstractCoroutine
类型代表的是一个协程,它的注释比较重要,直接能说明此类的作用:
它是一个协程体的基本实现的,抽象类。
它实现了[Continuation]
, [Job]
[CoroutineScope]
接口,说明它有如下属性:
3.2 续体
* Interface representing a continuation after a suspension point that returns a value of type `T`.
@SinceKotlin("1.3")
public interface Continuation<in T> {
* The context of the coroutine that corresponds to this continuation.
public val context: CoroutineContext
* Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the
* return value of the last suspension point.
public fun resumeWith(result: Result<T>)
Continuation
类型代表一个在挂起后,协程代码要继续往下执行的续体 (代码接下执行的续点) 。关键的有:
context
:此续体执行的上下文环境。
resumeWith
:欲恢复此续体继续执行时调用此函数。