【Kotlin】协程的字节码原理

前言

协程是Koltin语言最重要的特性之一,也是最难理解的特性。网上关于kotlin协程的描述也是五花八门,有人说它是轻量级线程,有人说它是无阻塞式挂起,有人说它是一个异步框架等等,众说纷芸。甚至还有人出了书籍专门介绍kotlin协程。

笔者刚开始接触这个概念也是一头雾水:什么叫轻量级线程?难道它是一个操作系统级别的任务调度器吗?闻所未闻呀。

后来才知道协程它其实不是线程,它只是一个语言层面的东西,确切地讲它是一个轻量级的线程框架,主要功能是可以实现简洁的线程切换,避免了直接使用Thread导致的回调地狱。也可以说它用同步的方式实现异步操作。

看别人怎么介绍,还不如亲眼看一看协程的字节码,其实没那么难。

笔者原创,转载请注明来源:https://blog.csdn.net/devnn/article/details/135610313

协程字节码

在MainActivity的onCreate中写一段协程的代码:

kotlin 复制代码
lifecycleScope.launchWhenResumed {
      Log.i("MainActivity", "launchWhenResumed,isMainThread:${Thread.currentThread().id == Looper.getMainLooper().thread.id}")//这里打印true
      Log.i("MainActivity", "launchWhenResumed,threadId:${Thread.currentThread().id}")//这里threadId打印2也就是主线程
      //以下两个代码块是串行执行的
      withContext(Dispatchers.Main) {
          Thread.sleep(10000)
          Log.i("MainActivity", "launchWhenResumed,withContext1,threadId:${Thread.currentThread().id}")
      }
      withContext(Dispatchers.IO) {
          Log.i("MainActivity", "launchWhenResumed,withContext2,threadId:${Thread.currentThread().id}")
      }
 }

笔者用lifecycleScope创建了一个协程,用其它方式创建也是一样的,比如MainScope()、GlobalScope、viewModelScope等等,这些方式的区别不是本文介绍的重点。

以上代码的字节码内容如下:

java 复制代码
 L33
    LINENUMBER 126 L33
    ALOAD 0
    CHECKCAST androidx/lifecycle/LifecycleOwner
    INVOKESTATIC androidx/lifecycle/LifecycleOwnerKt.getLifecycleScope (Landroidx/lifecycle/LifecycleOwner;)Landroidx/lifecycle/LifecycleCoroutineScope;
    NEW com/devnn/demo/MainActivity$onCreate$6
    DUP
    ACONST_NULL
    INVOKESPECIAL com/devnn/demo/MainActivity$onCreate$6.<init> (Lkotlin/coroutines/Continuation;)V
    CHECKCAST kotlin/jvm/functions/Function2
    INVOKEVIRTUAL androidx/lifecycle/LifecycleCoroutineScope.launchWhenResumed (Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job;
    POP

协程代码块被封装成了一个匿名内部类,匿名内部类继承了SuspendLambda(它实现了Continuation接口),同时实现了Function2接口,匿名内部类的构造方法需要一个Continuation参数,以上传null。

这个匿名内部类com/devnn/demo/MainActivity\$onCreate\$6.class内容如下:

java 复制代码
final class com.devnn.demo.MainActivity$onCreate$6 extends kotlin.coroutines.jvm.internal.SuspendLambda implements kotlin.jvm.functions.Function2<kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super kotlin.Unit>, java.lang.Object> {
  int label;

  com.devnn.demo.MainActivity$onCreate$6(kotlin.coroutines.Continuation<? super com.devnn.demo.MainActivity$onCreate$6>);
    Code:
       0: aload_0
       1: iconst_2
       2: aload_1
       3: invokespecial #13                 // Method kotlin/coroutines/jvm/internal/SuspendLambda."<init>":(ILkotlin/coroutines/Continuation;)V
       6: return

  public final java.lang.Object invokeSuspend(java.lang.Object);
    Code:
       0: invokestatic  #39                 // Method kotlin/coroutines/intrinsics/IntrinsicsKt.getCOROUTINE_SUSPENDED:()Ljava/lang/Object;
       3: astore_2
       4: aload_0
       5: getfield      #43                 // Field label:I
       8: tableswitch   { // 0 to 2
                     0: 36
                     1: 134
                     2: 176
               default: 186
          }
      36: aload_1
      37: invokestatic  #49                 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V
      40: ldc           #51                 // String MainActivity
      42: ldc           #53                 // String launchWhenResumed,isMainThread:
      44: invokestatic  #59                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
      47: invokevirtual #63                 // Method java/lang/Thread.getId:()J
      50: invokestatic  #69                 // Method android/os/Looper.getMainLooper:()Landroid/os/Looper;
      53: invokevirtual #72                 // Method android/os/Looper.getThread:()Ljava/lang/Thread;
      56: invokevirtual #63                 // Method java/lang/Thread.getId:()J
      59: lcmp
      60: ifne          67
      63: iconst_1
      64: goto          68
      67: iconst_0
      68: invokestatic  #78                 // Method kotlin/coroutines/jvm/internal/Boxing.boxBoolean:(Z)Ljava/lang/Boolean;
      71: invokestatic  #84                 // Method kotlin/jvm/internal/Intrinsics.stringPlus:(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
      74: invokestatic  #89                 // Method android/util/Log.i:(Ljava/lang/String;Ljava/lang/String;)I
      77: pop
      78: ldc           #51                 // String MainActivity
      80: ldc           #91                 // String launchWhenResumed,threadId:
      82: invokestatic  #59                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
      85: invokevirtual #63                 // Method java/lang/Thread.getId:()J
      88: invokestatic  #95                 // Method kotlin/coroutines/jvm/internal/Boxing.boxLong:(J)Ljava/lang/Long;
      91: invokestatic  #84                 // Method kotlin/jvm/internal/Intrinsics.stringPlus:(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
      94: invokestatic  #89                 // Method android/util/Log.i:(Ljava/lang/String;Ljava/lang/String;)I
      97: pop
      98: invokestatic  #101                // Method kotlinx/coroutines/Dispatchers.getMain:()Lkotlinx/coroutines/MainCoroutineDispatcher;
     101: checkcast     #103                // class kotlin/coroutines/CoroutineContext
     104: new           #105                // class com/devnn/demo/MainActivity$onCreate$6$1
     107: dup
     108: aconst_null
     109: invokespecial #107                // Method com/devnn/demo/MainActivity$onCreate$6$1."<init>":(Lkotlin/coroutines/Continuation;)V
     112: checkcast     #7                  // class kotlin/jvm/functions/Function2
     115: aload_0
     116: checkcast     #109                // class kotlin/coroutines/Continuation
     119: aload_0
     120: iconst_1
     121: putfield      #43                 // Field label:I
     124: invokestatic  #115                // Method kotlinx/coroutines/BuildersKt.withContext:(Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
     127: dup
     128: aload_2
     129: if_acmpne     139
     132: aload_2
     133: areturn
     134: aload_1
     135: invokestatic  #49                 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V
     138: aload_1
     139: pop
     140: invokestatic  #119                // Method kotlinx/coroutines/Dispatchers.getIO:()Lkotlinx/coroutines/CoroutineDispatcher;
     143: checkcast     #103                // class kotlin/coroutines/CoroutineContext
     146: new           #121                // class com/devnn/demo/MainActivity$onCreate$6$2
     149: dup
     150: aconst_null
     151: invokespecial #122                // Method com/devnn/demo/MainActivity$onCreate$6$2."<init>":(Lkotlin/coroutines/Continuation;)V
     154: checkcast     #7                  // class kotlin/jvm/functions/Function2
     157: aload_0
     158: checkcast     #109                // class kotlin/coroutines/Continuation
     161: aload_0
     162: iconst_2
     163: putfield      #43                 // Field label:I
     166: invokestatic  #115                // Method kotlinx/coroutines/BuildersKt.withContext:(Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
     169: dup
     170: aload_2
     171: if_acmpne     181
     174: aload_2
     175: areturn
     176: aload_1
     177: invokestatic  #49                 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V
     180: aload_1
     181: pop
     182: getstatic     #128                // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;
     185: areturn
     186: new           #130                // class java/lang/IllegalStateException
     189: dup
     190: ldc           #132                // String call to 'resume' before 'invoke' with coroutine
     192: invokespecial #135                // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
     195: athrow

  public final kotlin.coroutines.Continuation<kotlin.Unit> create(java.lang.Object, kotlin.coroutines.Continuation<?>);
    Code:
       0: new           #2                  // class com/devnn/demo/MainActivity$onCreate$6
       3: dup
       4: aload_2
       5: invokespecial #145                // Method "<init>":(Lkotlin/coroutines/Continuation;)V
       8: checkcast     #109                // class kotlin/coroutines/Continuation
      11: areturn

  public final java.lang.Object invoke(kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super kotlin.Unit>);
    Code:
       0: aload_0
       1: aload_1
       2: aload_2
       3: invokevirtual #151                // Method create:(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
       6: checkcast     #2                  // class com/devnn/demo/MainActivity$onCreate$6
       9: getstatic     #128                // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;
      12: invokevirtual #153                // Method invokeSuspend:(Ljava/lang/Object;)Ljava/lang/Object;
      15: areturn

  public java.lang.Object invoke(java.lang.Object, java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #159                // class kotlinx/coroutines/CoroutineScope
       5: aload_2
       6: checkcast     #109                // class kotlin/coroutines/Continuation
       9: invokevirtual #161                // Method invoke:(Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
      12: areturn
}

这个匿名内部类只有一个字段就是int类型的label。同时它的主要业务逻辑代码在invokeSuspend函数中,这个函数里面有一个tableswitch的判断,根据label的值,判断跳转到哪一块代码执行。看这个有点类似有限状态机。

Function2接口内容:

kotlin 复制代码
public interface Function2<in P1, in P2, out R> : Function<R> {
   /** Invokes the function with the specified arguments. */
  public operator fun invoke(p1: P1, p2: P2): R
}

MainActivity的协程中有两段withContext{}代码块,它们也被封装成了匿名内部类,这个匿名内部类跟外部的协程代码块一样,继承了SuspendLambda类,同时实现了Function2接口。

第一个withContext代码块的匿名内部类是com/devnn/demo/MainActivity\$onCreate\$6\$1.class

第二个withContext代码块的匿名内部类是com/devnn/demo/MainActivity\$onCreate\$6\$2.class

转载请注明来源:

https://blog.csdn.net/devnn/article/details/135610313

com/devnn/demo/MainActivity\$onCreate\$6\$1.class的内容如下:

java 复制代码
final class com.devnn.demo.MainActivity$onCreate$6$1 extends kotlin.coroutines.jvm.internal.SuspendLambda implements kotlin.jvm.functions.Function2<kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super java.lang.Integer>, java.lang.Object> {
  int label;

  com.devnn.demo.MainActivity$onCreate$6$1(kotlin.coroutines.Continuation<? super com.devnn.demo.MainActivity$onCreate$6$1>);
    Code:
       0: aload_0
       1: iconst_2
       2: aload_1
       3: invokespecial #13                 // Method kotlin/coroutines/jvm/internal/SuspendLambda."<init>":(ILkotlin/coroutines/Continuation;)V
       6: return

  public final java.lang.Object invokeSuspend(java.lang.Object);
    Code:
       0: invokestatic  #37                 // Method kotlin/coroutines/intrinsics/IntrinsicsKt.getCOROUTINE_SUSPENDED:()Ljava/lang/Object;
       3: pop
       4: aload_0
       5: getfield      #41                 // Field label:I
       8: tableswitch   { // 0 to 0
                     0: 28
               default: 61
          }
      28: aload_1
      29: invokestatic  #47                 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V
      32: ldc2_w        #48                 // long 10000l
      35: invokestatic  #55                 // Method java/lang/Thread.sleep:(J)V
      38: ldc           #57                 // String MainActivity
      40: ldc           #59                 // String launchWhenResumed,withContext1,threadId:
      42: invokestatic  #63                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
      45: invokevirtual #67                 // Method java/lang/Thread.getId:()J
      48: invokestatic  #73                 // Method kotlin/coroutines/jvm/internal/Boxing.boxLong:(J)Ljava/lang/Long;
      51: invokestatic  #79                 // Method kotlin/jvm/internal/Intrinsics.stringPlus:(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
      54: invokestatic  #84                 // Method android/util/Log.i:(Ljava/lang/String;Ljava/lang/String;)I
      57: invokestatic  #88                 // Method kotlin/coroutines/jvm/internal/Boxing.boxInt:(I)Ljava/lang/Integer;
      60: areturn
      61: new           #90                 // class java/lang/IllegalStateException
      64: dup
      65: ldc           #92                 // String call to 'resume' before 'invoke' with coroutine
      67: invokespecial #95                 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
      70: athrow

  public final kotlin.coroutines.Continuation<kotlin.Unit> create(java.lang.Object, kotlin.coroutines.Continuation<?>);
    Code:
       0: new           #2                  // class com/devnn/demo/MainActivity$onCreate$6$1
       3: dup
       4: aload_2
       5: invokespecial #102                // Method "<init>":(Lkotlin/coroutines/Continuation;)V
       8: checkcast     #104                // class kotlin/coroutines/Continuation
      11: areturn

  public final java.lang.Object invoke(kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super java.lang.Integer>);
    Code:
       0: aload_0
       1: aload_1
       2: aload_2
       3: invokevirtual #110                // Method create:(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
       6: checkcast     #2                  // class com/devnn/demo/MainActivity$onCreate$6$1
       9: getstatic     #116                // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;
      12: invokevirtual #118                // Method invokeSuspend:(Ljava/lang/Object;)Ljava/lang/Object;
      15: areturn

  public java.lang.Object invoke(java.lang.Object, java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #124                // class kotlinx/coroutines/CoroutineScope
       5: aload_2
       6: checkcast     #104                // class kotlin/coroutines/Continuation
       9: invokevirtual #126                // Method invoke:(Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
      12: areturn
}

com.devnn.demo.MainActivity$onCreate$6$2的内容如下:

java 复制代码
final class com.devnn.demo.MainActivity$onCreate$6$2 extends kotlin.coroutines.jvm.internal.SuspendLambda implements kotlin.jvm.functions.Function2<kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super kotlin.Unit>, java.lang.Object> {
  int label;

  com.devnn.demo.MainActivity$onCreate$6$2(kotlin.coroutines.Continuation<? super com.devnn.demo.MainActivity$onCreate$6$2>);
    Code:
       0: aload_0
       1: iconst_2
       2: aload_1
       3: invokespecial #13                 // Method kotlin/coroutines/jvm/internal/SuspendLambda."<init>":(ILkotlin/coroutines/Continuation;)V
       6: return

  public final java.lang.Object invokeSuspend(java.lang.Object);
    Code:
       0: invokestatic  #37                 // Method kotlin/coroutines/intrinsics/IntrinsicsKt.getCOROUTINE_SUSPENDED:()Ljava/lang/Object;
       3: pop
       4: aload_0
       5: getfield      #41                 // Field label:I
       8: tableswitch   { // 0 to 0
                     0: 28
               default: 56
          }
      28: aload_1
      29: invokestatic  #47                 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V
      32: ldc           #49                 // String MainActivity
      34: ldc           #51                 // String launchWhenResumed,withContext2,threadId:
      36: invokestatic  #57                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
      39: invokevirtual #61                 // Method java/lang/Thread.getId:()J
      42: invokestatic  #67                 // Method kotlin/coroutines/jvm/internal/Boxing.boxLong:(J)Ljava/lang/Long;
      45: invokestatic  #73                 // Method kotlin/jvm/internal/Intrinsics.stringPlus:(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
      48: invokestatic  #78                 // Method android/util/Log.i:(Ljava/lang/String;Ljava/lang/String;)I
      51: pop
      52: getstatic     #84                 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;
      55: areturn
      56: new           #86                 // class java/lang/IllegalStateException
      59: dup
      60: ldc           #88                 // String call to 'resume' before 'invoke' with coroutine
      62: invokespecial #91                 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
      65: athrow

  public final kotlin.coroutines.Continuation<kotlin.Unit> create(java.lang.Object, kotlin.coroutines.Continuation<?>);
    Code:
       0: new           #2                  // class com/devnn/demo/MainActivity$onCreate$6$2
       3: dup
       4: aload_2
       5: invokespecial #98                 // Method "<init>":(Lkotlin/coroutines/Continuation;)V
       8: checkcast     #100                // class kotlin/coroutines/Continuation
      11: areturn

  public final java.lang.Object invoke(kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super kotlin.Unit>);
    Code:
       0: aload_0
       1: aload_1
       2: aload_2
       3: invokevirtual #106                // Method create:(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
       6: checkcast     #2                  // class com/devnn/demo/MainActivity$onCreate$6$2
       9: getstatic     #84                 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;
      12: invokevirtual #108                // Method invokeSuspend:(Ljava/lang/Object;)Ljava/lang/Object;
      15: areturn

  public java.lang.Object invoke(java.lang.Object, java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #114                // class kotlinx/coroutines/CoroutineScope
       5: aload_2
       6: checkcast     #100                // class kotlin/coroutines/Continuation
       9: invokevirtual #116                // Method invoke:(Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
      12: areturn
}

每次执行新的匿名内部类代码(就是withContext代码块)时,就把当前这个匿名内部类传递给了新的匿名内部类。注意每个匿名内部类都是Continuation的实现。看到这里就知道其实Continuation就是一个回调。这其实就跟Java的回调一样,只是Kotlin隐式地实现了回调,并且加了状态机机制。每次执行一段suspend代码之后,将状态值修改成新值,执行回调时就知道走哪一段代码块。

转载请注明来源:

https://blog.csdn.net/devnn/article/details/135610313

相关推荐
Kapaseker40 分钟前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴43 分钟前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭11 小时前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab12 小时前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe17 小时前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农1 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
鹏程十八少1 天前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
Kapaseker1 天前
一杯美式搞定 Kotlin 空安全
android·kotlin
三少爷的鞋1 天前
Android 协程时代,Handler 应该退休了吗?
android
火柴就是我2 天前
让我们实现一个更好看的内部阴影按钮
android·flutter