这一篇需要对协程有一定理解,从源码角度深入理解协程的挂起与恢复。
直接上代码
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 (completion);
return var3;
}
public final Object invoke(Object var1, Object var2) {
return (()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 ) {
$continuation = ()var0;
if (((()$continuation).label & Integer.MIN_VALUE) != 0) {
(()$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 = (()$continuation).result;
var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch((()$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
(()$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();
(()$continuation).label = 2;
var10000 = deal2(num, (Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
}
User user = (User)var10000;
(()$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 ) {
$continuation = ()var0;
if (((()$continuation).label & Integer.MIN_VALUE) != 0) {
(()$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 = (()$continuation).result;
Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch((()$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
(()$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 ) {
$continuation = ()var1;
if (((()$continuation).label & Integer.MIN_VALUE) != 0) {
(()$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 = (()$continuation).result;
Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
User user;
switch((()$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
user = new User(11, "1");
(()$continuation).L$0 = user;
(()$continuation).label = 1;
if (DelayKt.delay(2000L, (Continuation)$continuation) == var5) {
return var5;
}
break;
case 1:
user = (User)(()$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 ) {
$continuation = ()var1;
if (((()$continuation).label & Integer.MIN_VALUE) != 0) {
(()$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 = (()$continuation).result;
Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch((()$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
(()$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 ) {
$continuation = ()var0;
if (((()$continuation).label & Integer.MIN_VALUE) != 0) {
(()$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 = (()$continuation).result;
var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch((()$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
(()$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();
(()$continuation).label = 2;
var10000 = deal2(num, (Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
}
User user = (User)var10000;
(()$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 (completion);
return var3;
}
public final Object invoke(Object var1, Object var2) {
return (()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 来源:稀土掘金