前言
协程是Koltin语言最重要的特性之一,也是最难理解的特性。网上关于kotlin协程的描述也是五花八门,有人说它是轻量级线程,有人说它是无阻塞式挂起,有人说它是一个异步框架等等,众说纷芸。甚至还有人出了书籍专门介绍kotlin协程。
笔者刚开始接触这个概念也是一头雾水:什么叫轻量级线程?难道它是一个操作系统级别的任务调度器吗?闻所未闻呀。
后来才知道协程它其实不是线程,它只是一个语言层面的东西,确切地讲它是一个轻量级的线程框架,主要功能是可以实现简洁的线程切换,避免了直接使用Thread导致的回调地狱。也可以说它用同步的方式实现异步操作。
看别人怎么介绍,还不如亲眼看一看协程的字节码,其实没那么难。
笔者原创,转载请注明来源:https://blog.csdn.net/devnn/article/details/135610313
协程字节码
在MainActivity的onCreate
中写一段协程的代码:
lifecycleScope.launchWhenResumed {Log.i("MainActivity", "launchWhenResumed,isMainThread:${Thread.currentThread().id == Looper.getMainLooper().thread.id}")//这里打印trueLog.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等等,这些方式的区别不是本文介绍的重点。
以上代码的字节码内容如下:
L33LINENUMBER 126 L33ALOAD 0CHECKCAST androidx/lifecycle/LifecycleOwnerINVOKESTATIC androidx/lifecycle/LifecycleOwnerKt.getLifecycleScope (Landroidx/lifecycle/LifecycleOwner;)Landroidx/lifecycle/LifecycleCoroutineScope;NEW com/devnn/demo/MainActivity$onCreate$6DUPACONST_NULLINVOKESPECIAL com/devnn/demo/MainActivity$onCreate$6.<init> (Lkotlin/coroutines/Continuation;)VCHECKCAST kotlin/jvm/functions/Function2INVOKEVIRTUAL androidx/lifecycle/LifecycleCoroutineScope.launchWhenResumed (Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job;POP
协程代码块被封装成了一个匿名内部类,匿名内部类继承了SuspendLambda
(它实现了Continuation
接口),同时实现了Function2接口,匿名内部类的构造方法需要一个Continuation参数,以上传null。
这个匿名内部类com/devnn/demo/MainActivity\$onCreate\$6.class
内容如下:
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_01: iconst_22: aload_13: invokespecial #13 // Method kotlin/coroutines/jvm/internal/SuspendLambda."<init>":(ILkotlin/coroutines/Continuation;)V6: returnpublic final java.lang.Object invokeSuspend(java.lang.Object);Code:0: invokestatic #39 // Method kotlin/coroutines/intrinsics/IntrinsicsKt.getCOROUTINE_SUSPENDED:()Ljava/lang/Object;3: astore_24: aload_05: getfield #43 // Field label:I8: tableswitch { // 0 to 20: 361: 1342: 176default: 186}36: aload_137: invokestatic #49 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V40: ldc #51 // String MainActivity42: ldc #53 // String launchWhenResumed,isMainThread:44: invokestatic #59 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;47: invokevirtual #63 // Method java/lang/Thread.getId:()J50: 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:()J59: lcmp60: ifne 6763: iconst_164: goto 6867: iconst_068: 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;)I77: pop78: ldc #51 // String MainActivity80: ldc #91 // String launchWhenResumed,threadId:82: invokestatic #59 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;85: invokevirtual #63 // Method java/lang/Thread.getId:()J88: 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;)I97: pop98: invokestatic #101 // Method kotlinx/coroutines/Dispatchers.getMain:()Lkotlinx/coroutines/MainCoroutineDispatcher;101: checkcast #103 // class kotlin/coroutines/CoroutineContext104: new #105 // class com/devnn/demo/MainActivity$onCreate$6$1107: dup108: aconst_null109: invokespecial #107 // Method com/devnn/demo/MainActivity$onCreate$6$1."<init>":(Lkotlin/coroutines/Continuation;)V112: checkcast #7 // class kotlin/jvm/functions/Function2115: aload_0116: checkcast #109 // class kotlin/coroutines/Continuation119: aload_0120: iconst_1121: putfield #43 // Field label:I124: invokestatic #115 // Method kotlinx/coroutines/BuildersKt.withContext:(Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;127: dup128: aload_2129: if_acmpne 139132: aload_2133: areturn134: aload_1135: invokestatic #49 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V138: aload_1139: pop140: invokestatic #119 // Method kotlinx/coroutines/Dispatchers.getIO:()Lkotlinx/coroutines/CoroutineDispatcher;143: checkcast #103 // class kotlin/coroutines/CoroutineContext146: new #121 // class com/devnn/demo/MainActivity$onCreate$6$2149: dup150: aconst_null151: invokespecial #122 // Method com/devnn/demo/MainActivity$onCreate$6$2."<init>":(Lkotlin/coroutines/Continuation;)V154: checkcast #7 // class kotlin/jvm/functions/Function2157: aload_0158: checkcast #109 // class kotlin/coroutines/Continuation161: aload_0162: iconst_2163: putfield #43 // Field label:I166: invokestatic #115 // Method kotlinx/coroutines/BuildersKt.withContext:(Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;169: dup170: aload_2171: if_acmpne 181174: aload_2175: areturn176: aload_1177: invokestatic #49 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V180: aload_1181: pop182: getstatic #128 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;185: areturn186: new #130 // class java/lang/IllegalStateException189: dup190: ldc #132 // String call to 'resume' before 'invoke' with coroutine192: invokespecial #135 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V195: athrowpublic final kotlin.coroutines.Continuation<kotlin.Unit> create(java.lang.Object, kotlin.coroutines.Continuation<?>);Code:0: new #2 // class com/devnn/demo/MainActivity$onCreate$63: dup4: aload_25: invokespecial #145 // Method "<init>":(Lkotlin/coroutines/Continuation;)V8: checkcast #109 // class kotlin/coroutines/Continuation11: areturnpublic final java.lang.Object invoke(kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super kotlin.Unit>);Code:0: aload_01: aload_12: aload_23: invokevirtual #151 // Method create:(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;6: checkcast #2 // class com/devnn/demo/MainActivity$onCreate$69: getstatic #128 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;12: invokevirtual #153 // Method invokeSuspend:(Ljava/lang/Object;)Ljava/lang/Object;15: areturnpublic java.lang.Object invoke(java.lang.Object, java.lang.Object);Code:0: aload_01: aload_12: checkcast #159 // class kotlinx/coroutines/CoroutineScope5: aload_26: checkcast #109 // class kotlin/coroutines/Continuation9: invokevirtual #161 // Method invoke:(Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;12: areturn
}
这个匿名内部类只有一个字段就是int类型的label
。同时它的主要业务逻辑代码在invokeSuspend
函数中,这个函数里面有一个tableswitch
的判断,根据label的值,判断跳转到哪一块代码执行。看这个有点类似有限状态机。
Function2
接口内容: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
的内容如下:
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_01: iconst_22: aload_13: invokespecial #13 // Method kotlin/coroutines/jvm/internal/SuspendLambda."<init>":(ILkotlin/coroutines/Continuation;)V6: returnpublic final java.lang.Object invokeSuspend(java.lang.Object);Code:0: invokestatic #37 // Method kotlin/coroutines/intrinsics/IntrinsicsKt.getCOROUTINE_SUSPENDED:()Ljava/lang/Object;3: pop4: aload_05: getfield #41 // Field label:I8: tableswitch { // 0 to 00: 28default: 61}28: aload_129: invokestatic #47 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V32: ldc2_w #48 // long 10000l35: invokestatic #55 // Method java/lang/Thread.sleep:(J)V38: ldc #57 // String MainActivity40: 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:()J48: 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;)I57: invokestatic #88 // Method kotlin/coroutines/jvm/internal/Boxing.boxInt:(I)Ljava/lang/Integer;60: areturn61: new #90 // class java/lang/IllegalStateException64: dup65: ldc #92 // String call to 'resume' before 'invoke' with coroutine67: invokespecial #95 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V70: athrowpublic final kotlin.coroutines.Continuation<kotlin.Unit> create(java.lang.Object, kotlin.coroutines.Continuation<?>);Code:0: new #2 // class com/devnn/demo/MainActivity$onCreate$6$13: dup4: aload_25: invokespecial #102 // Method "<init>":(Lkotlin/coroutines/Continuation;)V8: checkcast #104 // class kotlin/coroutines/Continuation11: areturnpublic final java.lang.Object invoke(kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super java.lang.Integer>);Code:0: aload_01: aload_12: aload_23: invokevirtual #110 // Method create:(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;6: checkcast #2 // class com/devnn/demo/MainActivity$onCreate$6$19: getstatic #116 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;12: invokevirtual #118 // Method invokeSuspend:(Ljava/lang/Object;)Ljava/lang/Object;15: areturnpublic java.lang.Object invoke(java.lang.Object, java.lang.Object);Code:0: aload_01: aload_12: checkcast #124 // class kotlinx/coroutines/CoroutineScope5: aload_26: checkcast #104 // class kotlin/coroutines/Continuation9: invokevirtual #126 // Method invoke:(Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;12: areturn
}
com.devnn.demo.MainActivity$onCreate$6$2
的内容如下:
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_01: iconst_22: aload_13: invokespecial #13 // Method kotlin/coroutines/jvm/internal/SuspendLambda."<init>":(ILkotlin/coroutines/Continuation;)V6: returnpublic final java.lang.Object invokeSuspend(java.lang.Object);Code:0: invokestatic #37 // Method kotlin/coroutines/intrinsics/IntrinsicsKt.getCOROUTINE_SUSPENDED:()Ljava/lang/Object;3: pop4: aload_05: getfield #41 // Field label:I8: tableswitch { // 0 to 00: 28default: 56}28: aload_129: invokestatic #47 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V32: ldc #49 // String MainActivity34: 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:()J42: 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;)I51: pop52: getstatic #84 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;55: areturn56: new #86 // class java/lang/IllegalStateException59: dup60: ldc #88 // String call to 'resume' before 'invoke' with coroutine62: invokespecial #91 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V65: athrowpublic final kotlin.coroutines.Continuation<kotlin.Unit> create(java.lang.Object, kotlin.coroutines.Continuation<?>);Code:0: new #2 // class com/devnn/demo/MainActivity$onCreate$6$23: dup4: aload_25: invokespecial #98 // Method "<init>":(Lkotlin/coroutines/Continuation;)V8: checkcast #100 // class kotlin/coroutines/Continuation11: areturnpublic final java.lang.Object invoke(kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super kotlin.Unit>);Code:0: aload_01: aload_12: aload_23: invokevirtual #106 // Method create:(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;6: checkcast #2 // class com/devnn/demo/MainActivity$onCreate$6$29: getstatic #84 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;12: invokevirtual #108 // Method invokeSuspend:(Ljava/lang/Object;)Ljava/lang/Object;15: areturnpublic java.lang.Object invoke(java.lang.Object, java.lang.Object);Code:0: aload_01: aload_12: checkcast #114 // class kotlinx/coroutines/CoroutineScope5: aload_26: checkcast #100 // class kotlin/coroutines/Continuation9: 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