Kotlin协程系列文章:《一、Kotlin协程系列:协程的创立与发动,挂起和康复》

Kotlin协程系列文章:《二、Kotlin协程系列:协程的线程调度》

本文是剖析Kotlin协程系列文章的第一篇,在本篇中分为2大块剖析协程创立和发动流程;挂起和康复流程。其中在剖析协程的创立发动流程中,咱们将回答:

  1. kotlin编译器是怎么处理suspend Lambda的,或许说处理suspend要害字的?
  2. 传入协程lambda的代码是怎么被履行到的?

在剖析协程的挂起和康复流程中,咱们将回答:

  1. 协程挂起的本质是什么?又是怎么被挂起的?挂起会阻塞履行线程吗?
  2. 协程是怎么做到用同步调用的办法履行异步代码?
  3. 协程在挂起后,又是怎么康复履行的?

一、协程的创立与发动

一般来说敞开协程有2种办法:

  • CoroutineScope.launch()
  • CoroutineScope.async()

咱们以第一种来做为主流程剖析,其他办法殊途同归。

1.1 suspend Lambda表达式的编译处理

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //1. 发动一个协程
        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的编译产物

//MainActivity.class
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);
 }
//MainActivity$onCreate$1.class
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);
    }
    //1. 重写了BaseContinuationImpl.create()办法。
    @Override // kotlin.coroutines.jvm.internal.BaseContinuationImpl
    public final Continuation<Unit> create(Object obj, Continuation<?> continuation) {
        // 直接new了一个本身实例出来回来。
        return new MainActivity$onCreate$1(continuation);
    }
	...
    //2. 重写了BaseContinuationImpl.invokeSuspend()办法。这里是状况机完成核心办法。
    @Override // kotlin.coroutines.jvm.internal.BaseContinuationImpl
    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类型的实例,该类型实例有如下特色:

  1. 承继于SuspendLambda
  2. 完成了BaseContinuationImpl.create()办法,该办法用于创立本身实例。
  3. 完成了BaseContinuationImpl.invokeSuspend()办法,该办法包含了真实的协程逻辑代码,而且用状况机的办法完成了协程的挂起/康复逻辑。

为了探究主动生成的实例类型究竟是何物,咱们持续沿着它承继的SuspendLambda往上看。

1.1.2 SuspendLambda

@SinceKotlin("1.3")
// Suspension lambdas inherit from this class
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
}

能够看到SuspendLambda承继至ContinuationImpl

  1. SuspendLambda其实没有扩展什么实践功用,只扩展了个无关紧要的toString()办法。
  2. 留意这里将conmpletion成员变量持续传给ContinuationImpl父类。

接下来沿着ContinuationImpl一步步往上探究续体。

1.1.3 ContinuationImpl

internal abstract class ContinuationImpl(
    completion: Continuation<Any?>?,
    private val _context: CoroutineContext?
) : BaseContinuationImpl(completion) {
    //1.ContinuationImpl的Context运用的是completion?.context
    constructor(completion: Continuation<Any?>?) 
    : this(completion, completion?.context)
    public override val context: CoroutineContext
        get() = _context!!
    @Transient
    private var intercepted: Continuation<Any?>? = null
    //2. 获取拦截器
    public fun intercepted(): Continuation<Any?> =
        intercepted
            ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
                .also { intercepted = it }
    protected override fun releaseIntercepted() {
        ...
    }
}

ContinuationImpl承继至BaseContinuationImpl:

  1. 留意这里,ContinuationImpl所运用的协程上下文其有用的是completion.context。而将completion续体持续往父类传递。

  2. 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) {
                 // fail fast when trying to resume continuation without completion
                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
                }
            }
        }
    }
    //此办法在主动生成的SuspendLambda实例时会被完成,该办法用于完成协程的状况机。
    protected abstract fun invokeSuspend(result: Result<Any?>): Any?
    protected open fun releaseIntercepted() {
        // does nothing here, overridden in ContinuationImpl
    }
    //此办法在主动生成的SuspendLambda实例时会被完成,该办法用于创立该实例的。
    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 {
    //1. 仿制Context
    val newContext = newCoroutineContext(context)
    //2. 用Context创立一个新的续体,留意这个续体将会被作为完毕回调。
    val coroutine = if (start.isLazy)
    LazyStandaloneCoroutine(newContext, block) else
    //一般是进入这个分支。
    StandaloneCoroutine(newContext, active = true)
    //3.发动协程。
    coroutine.start(start, coroutine, block)
    return coroutine
}

一般的,咱们运用CoroutineScope.launch()发动一个协程,步进此办法源码有如下几个要点:

  1. 仿制Context,并默许运用了Dispatchers.Default
  2. 创立了一个新的协程体StandaloneCoroutine,后问咱们在本文中称之为协程体1,留意这个StandaloneCoroutine承继Continuation<T>,所以它其实也是个续体。
  3. 调用StandaloneCoroutine.start()办法,留意这里第二个参数又有把自己传入当作参数。详见1.2.2。

1.2.2 StandaloneCoroutine.start()

//AbstractCoroutine.kt
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
        //1.CoroutineStart.invoke()
        start(block, receiver, this)
    }
//CorountineStart.kt
 @InternalCoroutinesApi
public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit =
    when (this) {
        //2. 进入这个调用
        DEFAULT -> block.startCoroutineCancellable(receiver, completion)
        ATOMIC -> block.startCoroutine(receiver, completion)
        UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
        LAZY -> Unit // will start lazily
    }
  1. 这里其实是由于Kotlin的语法糖,所以实践调用了CoroutineStart.invoke()办法,由于在上面中,咱们用默许的CoroutineStart.DEFAULT。留意这里的receiver等于之前新建的coroutine,而这里的this也是coroutine
  2. 能够看到其实调用的是block的办法。

1.2.3 真实发动协程 startCoroutineCancellable()

//Cancellable.kt
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
    receiver: R, completion: Continuation<T>,
    onCancellation: ((cause: Throwable) -> Unit)? = null
) =
    runSafely(completion) {
        //1. 创立协程
        createCoroutineUnintercepted(receiver, completion)
            //2. 协程线程调度切换,具体见1.2.5
            .intercepted()
            //3. 出发协程敞开工作,详见第1.2.6
            .resumeCancellableWith(Result.success(Unit), onCancellation)
    }

能够看到,startCoroutineCancellable其实是(suspend (R) -> T)类型的一个扩展函数。这里三个调用步步要害,但是在这个末节中首要讨论协程的创立,所以咱们只关怀1流程。

createCoroutineUnintercepted()办法追寻进去会到IntrinsicsKt.class类,在这里看不到具体完成,由于这是渠道相关性,所以需求直接定位IntrinsicsJvm.class就能找到该办法在JVM虚拟机上的完成。

1.2.4 createCoroutineUnintercepted()

//IntrinsicsJvm.class
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)
    	//1. 进入这里
        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()

//1. 重写了BaseContinuationImpl.create()办法。
    @Override // kotlin.coroutines.jvm.internal.BaseContinuationImpl
    public final Continuation<Unit> create(Object obj, Continuation<?> continuation) {
        // 直接new了一个本身实例出来回来。
        return new MainActivity$onCreate$1(continuation);
    }
  1. 能够看到这个办法里就直接new了一个MainActivity$onCreate$1类型实例,具体能够参阅1.1末节。后续咱们称此为续体1。
  2. 这里需求留意到的是,构建是传入了一个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) {
    //如是是调度器续体,那么将会进入resumeCancellableWith(), 调度流程。
    is DispatchedContinuation -> resumeCancellableWith(result, onCancellation)
    //进入这个分支,详见1.1.4末节BaseContinuationImpl.java 完成
    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?> =
				...
                // 调用协程续体代码,流程见1.2.6.2
                val outcome = invokeSuspend(param)
               ...
            }
        }
    }

简化了代码,只保留了主流程上的要害代码。

  1. 此办法是续体1的父类的默许完成。

  2. 能够看到调用了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 总结

  1. 经过launch发动协程时,会先创立一个StandaloneCoroutine续体作为完毕回调,终究传给生成的SuspendLambda实例作为其成员变量。
  2. createCoroutineUnintercepted()办法的完成其实是在IntrinsicsJvm.class文件里,他调用了生成的实例的create()办法,终究new出了一个MainActivity$onCreate$1类型实例。
  3. 协程代码的履行触发流程:BaseContinuationImpl.resumeWith()->BaseContinuationImpl.resume()->MainActivity$onCreate$1.invokeSuspend()

二、协程挂起/康复

在前面末节中,咱们用一个简略的协程比如,剖析了协程发动进程的中细节,其中要点包括:

  1. SuspendLambda的主动生成。
  2. 新建外层续体作为completion回调。
  3. 触发协程事务逻辑代码的流程剖析。

在本末节中,咱们即将讨论的是协程的挂起/康复机制。协程的挂起/康复机制是协程的一大特色,它使得本来异步编程能够像同步编程办法一样直观明了,而不必陷入层层回调中。为了能够剖析此流程,咱们需求稍微复杂一点的协程代码比如:

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.")
            //1. 详见2.1.1,调用job1挂起函数,
            job1()
            Log.d(TAG, "onCreate: ready to run job2.")
            //2. 详见2.1.2,调用job2
            job2()
        }
    }
    suspend fun job1() {
        delay(1000)
        Log.d(TAG, "job1: finish.")
    }
    suspend fun job2() {
        Log.d(TAG, "job2: finish.")
    }
}

在敞开本末节的剖析前,先简略简明的阐述下协程神秘的挂起/康复机制为什么能够化异步为同步:

  1. 在协程内,当履行到一个job1 挂起函数时,会将外层的续体Continuation作为参数传入,修改状况机的label标志位。
  2. 并挂起函数履行异步事务逻辑并同步的回来一个SUSPEND标志位,此刻外层协程办法会被return。
  3. 当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)
    {
    //1. 状况机标志位
      int label;
	...
	  //2. 协程实践事务逻辑代码。
      public final Object invokeSuspend(Object paramObject)
      {
        //留意这里localObject=SUSPEND标志位
        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);
          // 3. local1 为外层续体。
          local1 = this;
          Log.d(local1.this$0.getTAG(), "onCreate: log in continue.");
          MainActivity localMainActivity1 = local1.this$0;
          //将外层续体赋值给localContinuation2
          Continuation localContinuation1 = (Continuation)local1;
          // 将状况机标志位置为1
          local1.label = 1;
          //4. 调用job1函数,一起将外层续体作为参数传入。
          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函数时:

  1. 此刻label==0,进入case 0。
  2. 将label置为1。
  3. 将续体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. 先将外层续体1作为参数生成一个续体完成类,咱们称之为job1续体,并仿制给local11
  2. 进入case0,并更新label=1。
  3. 调用delay函数,这个函数是一个延时函数,这里并不打算深究进去,但是能够猜想到的是:
    1. 在delay函数里面会敞开一个守时任务并先回来一个COROUTINE_SUSPENDED标志位。
    2. 时刻到时,守时任务触发时,调用local12.resumeWith()切换到job1续体完成类代码,
    3. resumeWith经过调用进入到local12.inovkeSuspen()函数。
  1. 在流程3中,由于调用了delay函数,其回来SUPEND标志位,判别成立,函数回来。所以流程又回到了2.1.1#4步骤:此刻又在续体1中断定了一次:COROUTINE_SUSPENDED != COROUTINE_SUSPENDED断定不成功,所以returninovkeSuspend()函数。

  2. 此刻便回到咱们调用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) {
                 // fail fast when trying to resume continuation without completion
                val completion = completion!!
                 // fail fast when trying to resume continuation without completion
                val outcome: Result<Any?> =
                    try {
                        // 此办法回来COROUTINE_SUSPENDED 标志位
                        val outcome = invokeSuspend(param)
                        //此刻判别成功return。
                        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
                }
            }
        }
    }

此刻回来挂起标志位,所以会将此办法直接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);
        //1. 调用回job1()函数
        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办法,此刻:

  1. 此刻label==1,所以进入case1,随后return Unit.INSTANCE
  2. 依据之前的剖析,此刻会直接回来到local11.resumeWith() 详见2.2.2

2.2.2 康复 Job1.resumeWith()

public final override fun resumeWith(result: Result<Any?>) {
        ...
        while (true) {
           	...
            with(current) {
                 // fail fast when trying to resume continuation without completion
                val completion = completion!!
                 // fail fast when trying to resume continuation without completion
                val outcome: Result<Any?> =
                    try {
                        //1. 此办法回来Unit.INSTANCE 标志位
                        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 {
                    //2. completion 是DispatchedCoroutine类型,所以进入此流程;(TODO: 这里待研讨)
                    // top-level completion reached -- invoke and return
                    completion.resumeWith(outcome)
                    return
                }
            }
        }
    }
  1. 从2.3 能够知道回来的是Unit.INSTANCE,所以进入到下面的if判别。
  2. 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)
      {
        //留意这里localObject=SUSPEND标志位
        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;
      }

再次回到这个函数时:

  1. label由于之前置为1了,所以进入case1流程
  2. 先置label=2
  3. 和调用job1时一样, 将外层续体作为参数传入job2。

自此,主协程便又康复了运行,接着去履行job2的代码了。后续的逻辑如果由挂起就仍是和job1的流程共同,如果job2里面没调用挂起函数,那么也就没有挂起标志,所以直接同步履行后完毕主协程的事务逻辑。

2.3 总结

  1. suspend 润饰的函数会经过CPS处理后,为函数增加一个Continuation的入参,而这个入参的值便是调用此函数的父协程。

  2. 协程的挂起在于:履行到异步步耗时机制时,它供给了一个SUSPEND标志以供回来,协程的状况机检测到这个标识位后便return了这个函数,此为挂起。

  3. 协程的康复在于:当耗时或许异步任务完毕时,能够调用Continuation.resumeWith()康复协程,终究此函数的协程走完,便会调用completion.resumeWith(outcome),这个completion便是之前传入的父协程。自此父协程康复状况机的运转,接着履行后续的代码。

  4. 协程的挂起不是当时协程一直阻塞在挂起函数的履行,而是直接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类型代表的是一个协程,它的注释比较重要,直接能阐明此类的效果:

  1. 它是一个协程体的基本完成的,抽象类
  2. 它完成了[Continuation], [Job] [CoroutineScope]接口,阐明它有如下特点:
    1. [Continuation]:是一个续体目标,能够经过回调resume康复此协程。
    2. [Job] :是一个协程作业目标,能够撤销和获取作业状况。
    3. [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:欲康复此续体持续履行时调用此函数。