这一篇需要对协程有一定理解,从源码角度深入理解协程的挂起与恢复。
直接上代码
GlobalScope.launch {
val data = goodeTest()
print(data)
}
Thread.sleep(3000)
}
suspend fun goodTest(): String {
val num = deal1()
val user = deal2(num)
val strong = deal3(user)
return strong
}
suspend fun deal1(): Int {
delay(22)
return 2
}
suspend fun deal2(num: Int): User {
val user = User(11, "1")
delay(2000)
return user
}
suspend fun deal3(user: User): String {
delay(300)
return "ss"
}
上字节码。学习原理还是要看字节码。网上重组过的字节码理解起来总感觉差点意思。这次我直接上未修改的,带着一步一步理解协程是怎么挂起恢复的。可以直接跳过这个总览,最后再回来看这个总览。后面的我都会一点点解释。
package com.zs.myapplication.coroutine;
import com.zs.myapplication.bean.User;
import kotlin.Metadata;
import kotlin.ResultKt;
import kotlin.Unit;
import kotlin.coroutines.Continuation;
import kotlin.coroutines.CoroutineContext;
import kotlin.coroutines.intrinsics.IntrinsicsKt;
import kotlin.coroutines.jvm.internal.Boxing;
import kotlin.coroutines.jvm.internal.ContinuationImpl;
import kotlin.jvm.functions.Function2;
import kotlin.jvm.internal.Intrinsics;
import kotlinx.coroutines.BuildersKt;
import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.CoroutineStart;
import kotlinx.coroutines.DelayKt;
import kotlinx.coroutines.GlobalScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@Metadata(
mv = {1, 6, 0},
k = 2,
d1 = {"\u0000 \n\u0000\n\u0002\u0010\b\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0010\u000e\n\u0002\b\u0004\n\u0002\u0010\u0002\n\u0000\u001a\u0011\u0010\u0000\u001a\u00020\u0001H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0002\u001a\u0019\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u0001H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0006\u001a\u0019\u0010\u0007\u001a\u00020\b2\u0006\u0010\t\u001a\u00020\u0004H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\n\u001a\u0011\u0010\u000b\u001a\u00020\bH\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0002\u001a\u0006\u0010\f\u001a\u00020\r\u0082\u0002\u0004\n\u0002\b\u0019¨\u0006\u000e"},
d2 = {"deal1", "", "(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", "deal2", "Lcom/zs/myapplication/bean/User;", "num", "(ILkotlin/coroutines/Continuation;)Ljava/lang/Object;", "deal3", "", "user", "(Lcom/zs/myapplication/bean/User;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", "goodTest", "main", "", "My_Application.app.main"}
)
public final class GoodAnalisyKt {
public static final void main() {
BuildersKt.launch$default((CoroutineScope)GlobalScope.INSTANCE, (CoroutineContext)null, (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
Object var10000;
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
this.label = 1;
var10000 = GoodAnalisyKt.goodTest(this);
if (var10000 == var3) {
return var3;
}
break;
case 1:
ResultKt.throwOnFailure($result);
var10000 = $result;
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
String data = (String)var10000;
System.out.print(data);
return Unit.INSTANCE;
}
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, "completion");
Function2 var3 = new <anonymous constructor>(completion);
return var3;
}
public final Object invoke(Object var1, Object var2) {
return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
}
}), 3, (Object)null);
Thread.sleep(3000L);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
@Nullable
public static final Object goodTest(@NotNull Continuation var0) {
Object $continuation;
label37: {
if (var0 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var0;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label37;
}
}
$continuation = new ContinuationImpl(var0) {
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return GoodAnalisyKt.goodTest(this);
}
};
}
Object var10000;
label31: {
Object var6;
label30: {
Object $result = ((<undefinedtype>)$continuation).result;
var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
((<undefinedtype>)$continuation).label = 1;
var10000 = deal1((Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
break;
case 1:
ResultKt.throwOnFailure($result);
var10000 = $result;
break;
case 2:
ResultKt.throwOnFailure($result);
var10000 = $result;
break label30;
case 3:
ResultKt.throwOnFailure($result);
var10000 = $result;
break label31;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
int num = ((Number)var10000).intValue();
((<undefinedtype>)$continuation).label = 2;
var10000 = deal2(num, (Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
}
User user = (User)var10000;
((<undefinedtype>)$continuation).label = 3;
var10000 = deal3(user, (Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
}
String strong = (String)var10000;
return strong;
}
@Nullable
public static final Object deal1(@NotNull Continuation var0) {
Object $continuation;
label20: {
if (var0 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var0;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
$continuation = new ContinuationImpl(var0) {
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return GoodAnalisyKt.deal1(this);
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
((<undefinedtype>)$continuation).label = 1;
if (DelayKt.delay(22L, (Continuation)$continuation) == var3) {
return var3;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return Boxing.boxInt(2);
}
@Nullable
public static final Object deal2(int var0, @NotNull Continuation var1) {
Object $continuation;
label20: {
if (var1 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var1;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
$continuation = new ContinuationImpl(var1) {
// $FF: synthetic field
Object result;
int label;
Object L$0;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return GoodAnalisyKt.deal2(0, this);
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
User user;
switch(((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
user = new User(11, "1");
((<undefinedtype>)$continuation).L$0 = user;
((<undefinedtype>)$continuation).label = 1;
if (DelayKt.delay(2000L, (Continuation)$continuation) == var5) {
return var5;
}
break;
case 1:
user = (User)((<undefinedtype>)$continuation).L$0;
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return user;
}
@Nullable
public static final Object deal3(@NotNull User var0, @NotNull Continuation var1) {
Object $continuation;
label20: {
if (var1 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var1;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
$continuation = new ContinuationImpl(var1) {
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return GoodAnalisyKt.deal3((User)null, this);
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
((<undefinedtype>)$continuation).label = 1;
if (DelayKt.delay(300L, (Continuation)$continuation) == var4) {
return var4;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return "ss";
}
}
1.我们先看 goodTest() 的字节码
@Nullable
public static final Object goodTest(@NotNull Continuation var0) {
Object $continuation;
label37: {
if (var0 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var0;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label37;
}
}
$continuation = new ContinuationImpl(var0) { 生成一个continuation类型的匿名类
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return GoodAnalisyKt.goodTest(this); //自己调自己
}
};
}
Object var10000;
label31: {
Object var6;
label30: {
Object $result = ((<undefinedtype>)$continuation).result;
var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
((<undefinedtype>)$continuation).label = 1;
var10000 = deal1((Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
break;
case 1:
ResultKt.throwOnFailure($result);
var10000 = $result;
break;
case 2:
ResultKt.throwOnFailure($result);
var10000 = $result;
break label30;
case 3:
ResultKt.throwOnFailure($result);
var10000 = $result;
break label31;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
int num = ((Number)var10000).intValue();
((<undefinedtype>)$continuation).label = 2;
var10000 = deal2(num, (Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
}
User user = (User)var10000;
((<undefinedtype>)$continuation).label = 3;
var10000 = deal3(user, (Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
}
String strong = (String)var10000;
return strong;
}
我们知道协程的启动是通过 GlobalScop.launch{} 方法,这里面会创建一个suspendLamada,并且也会向下传递一个continuation。所以 goodTest 的入参continuation就是从这个 suspendLamada 传入的,并且会在启动的时候调用方法 invokeSuspend() 方法。也就是总览java文件里面的 invokeSuspend() 方法,还是看一下吧,如下
public static final void main() {
BuildersKt.launch$default((CoroutineScope)GlobalScope.INSTANCE, (CoroutineContext)null, (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
Object var10000;
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
this.label = 1;
var10000 = GoodAnalisyKt.goodTest(this);
if (var10000 == var3) {
return var3;
}
break;
case 1:
ResultKt.throwOnFailure($result);
var10000 = $result;
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
String data = (String)var10000;
System.out.print(data);
return Unit.INSTANCE;
}
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, "completion");
Function2 var3 = new <anonymous constructor>(completion);
return var3;
}
public final Object invoke(Object var1, Object var2) {
return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
}
}), 3, (Object)null);
Thread.sleep(3000L);
}
这块会调用 invokeSuspend() 方法,然后根据label状态机 case 0 调用 goodTest() 方法并且传递 continution 。这个 contiuntion 又是包裹了 completion 的, completion 是调用 GlobalScope.launch{} 方法所创建的 StandaloneCoroutine 如下


这个 StandaloneCoroutine 是一个 AbstractCoroutine ,后面恢复的实话会用到这块。 接着 goodTest() 往下讲,在进入 switch 语句的 case 0 后,调用 deal1() 方法,然后在传入一个 continuation 这个 continuation 是包裹了父协程的 contiunation 多读写这几句理解下。 然后看下 deal1() 方法,也会创建一个匿名类集成自 ContinuationImpl 的 continution 并接受 goodTest() 传递下来的 continution ,然后同样进入 case0 ,调用 delay() 函数,传入自己创建的 ContinuationImpl ,然后返回 COROUTINE_SUSPENDED 一段时候会调用这个 continuation 的 resumeWith() 方法,这个时候就是恢复了,重点来了 看下 resumeWith() 方法的实现类

所以挂起就是在这块挂起的。当需要恢复的时候也是调用的这块。
还记得 delay() 方法我们我们传递进去的是哪个 continution 吗?对,就是 deal1() 方法里面创建的匿名类继承自 ContinuationImpl ,所以这块 current 就是自己的匿名类 continution ,也就是在 1 除。所以会调用 deal1() 方法自己的 suspendInvoke() 方法,因为在开始的时候也就是 2 处,我们将自 conpletion 进行了一次赋值,把要操作的 continution 换成了自己传递进来的 continution 也就是父协程的 continution ,所以 deal1() 恢复后,下一次循环调用的 invokeSuspend 就是父协程的 invokeSuspend() 方法,对于我们的demo也就是调用的 goodTest() 的 invokeSuspend() 方法,这样就是完成状态的流转。最后调用这个方法的时候也就是 goodTest() 方法调用 resumeWith() 方法时,传入的是 AbstractCoroutine 所以会进入 标注6 ,完成整个协程的恢复。
至此,协程挂起恢复原理总结完成。 总结下:
- 协程启动(调用 launch )的时候会有两个重要的 Continution ,一个是 AbstractCoroutine ,一个是 suspendLamada (suspendLamada继承自 BaseContinuationImpl )
- 当调用 suspend 函数时,会创建一个匿名类 ContinuationImpl (继承自 BaseContinuationImpl )并将父协程的 contiuntion 传递进来,挂起恢复都是在 BaseContinuationImpl 里面的 resumeWith() 的 while(true) 里面进行控制。
- 当函数挂起时(即函数返回 COROUTINE_SUSPENDED ),在 BaseContinuationImpl 里面return。
- 当函数恢复时,也是在 BaseContinuationImpl 里面调用了 resumeWith ,此时会替换 continution 为父协程的 continution 。出发父协程的 invokeSuspend 完成事件流转。
- 最终调用 AbstractCoroutine 类型的 continution 的 resumeWith() 方法,完成整个协程的恢复
作者:Lazurs链接:https://juejin.cn/post/7147534071978491934来源:*土稀**掘金