协程是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}")//这里打印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等等,这些方式的区别不是本文介绍的重点。
以上代码的字节码内容如下:
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
内容如下:
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."":(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."":(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."":(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."":(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 "":(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
接口内容: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_0
1: iconst_2
2: aload_1
3: invokespecial #13 // Method kotlin/coroutines/jvm/internal/SuspendLambda."":(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."":(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 "":(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
的内容如下:
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."":(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."":(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 "":(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