一文搞懂中统军统 (协程的基本知识)

这一篇需要对协程有一定理解,从源码角度深入理解协程的挂起与恢复。

直接上代码

    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 ,完成整个协程的恢复。

至此,协程挂起恢复原理总结完成。 总结下:

  1. 协程启动(调用 launch )的时候会有两个重要的 Continution ,一个是 AbstractCoroutine ,一个是 suspendLamada (suspendLamada继承自 BaseContinuationImpl )
  2. 当调用 suspend 函数时,会创建一个匿名类 ContinuationImpl (继承自 BaseContinuationImpl )并将父协程的 contiuntion 传递进来,挂起恢复都是在 BaseContinuationImpl 里面的 resumeWith() while(true) 里面进行控制。
  3. 当函数挂起时(即函数返回 COROUTINE_SUSPENDED ),在 BaseContinuationImpl 里面return。
  4. 当函数恢复时,也是在 BaseContinuationImpl 里面调用了 resumeWith ,此时会替换 continution 为父协程的 continution 。出发父协程的 invokeSuspend 完成事件流转。
  5. 最终调用 AbstractCoroutine 类型的 continution resumeWith() 方法,完成整个协程的恢复

作者:Lazurs链接:https://juejin.cn/post/7147534071978491934来源:*土稀**掘金