示例:
- fun main() {
-
- runBlocking {
- val a = getA()
- println(a)
- val b = getB(a)
- println(b)
- val c = getC(b)
- println(c)
- }
- }
-
- suspend fun getA(): String {
- withContext(Dispatchers.IO) {
- delay(2000L)
- }
- return "A content"
- }
-
- suspend fun getB(a: String): String {
- withContext(Dispatchers.IO) {
- delay(2000L)
- }
- return "$a B content"
- }
-
- suspend fun getC(b: String): String {
- withContext(Dispatchers.IO) {
- delay(2000L)
- }
- return "$b C content"
- }
-
-
- 输出
- A content
- A content B content
- A content B content C content
suspend关键字修饰的方法 就是 挂起函数。挂起函数具备挂起和恢复的能力。挂起就是将程序执行流程转移到其他线程,主线程不阻塞。挂起函数的本质是Callback。
Kotlin编译器检测到suspend关键字修饰的函数,会将挂起函数转换成带有CallBack的函数。
- suspend fun getA(): String {
- withContext(Dispatchers.IO) {
- delay(5000L)
- println("now in A process:" + Thread.currentThread())
- }
- /**
- * 这里的代码涉及挂起函数中的操作。
- */
- println("finish A process:" + Thread.currentThread())
- return "A content"
- }
将上述Kotlin代码转换成java代码。
- @Nullable
- public static final Object getA(@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 TestCoroutinue2Kt.getA(this);
- }
- };
- }
-
- Object $result = ((
)$continuation).result; - Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
- switch(((
)$continuation).label) { - case 0:
- ResultKt.throwOnFailure($result);
- CoroutineContext var10000 = (CoroutineContext)Dispatchers.getIO();
- Function2 var10001 = (Function2)(new Function2((Continuation)null) {
- int label;
-
- @Nullable
- public final Object invokeSuspend(@NotNull Object $result) {
- Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
- switch(this.label) {
- case 0:
- ResultKt.throwOnFailure($result);
- this.label = 1;
- if (DelayKt.delay(5000L, this) == var3) {
- return var3;
- }
- break;
- case 1:
- ResultKt.throwOnFailure($result);
- break;
- default:
- throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
- }
-
- String var2 = "now in A process:" + Thread.currentThread();
- System.out.println(var2);
- 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); - }
- });
- ((
)$continuation).label = 1; - if (BuildersKt.withContext(var10000, var10001, (Continuation)$continuation) == var4) {
- return var4;
- }
- break;
- case 1:
- ResultKt.throwOnFailure($result);
- break;
- default:
- throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
- }
-
- String var1 = "finish A process:" + Thread.currentThread();
- System.out.println(var1);
- return "A content";
- }
注意:runBlocking 的第二个参数 也是 传入一个 suspend修饰的函数 即挂起函数。
public actual fun runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T {
可以发现上面的Continuation 是一个带有泛型参数的Callback,这里的转换称为CPS转换,将原本的同步挂起函数转换成CallBack异步代码。
- /**
- * Interface representing a continuation after a suspension point that returns a value of type `T`.
- */
- @SinceKotlin("1.3")
- public interface Continuation<in T> {
- /**
- * The context of the coroutine that corresponds to this continuation.
- */
- public val context: CoroutineContext
-
- /**
- * Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the
- * return value of the last suspension point.
- */
- public fun resumeWith(result: Result<T>)
- }
注意:挂起函数,只能在协程中被调用,或者被其他挂起函数调用。
为什么挂起函数可以调用挂起函数,而普通函数不能调用挂起函数?
- fun main() {
- doA() //这里会报错
- }
-
-
- suspend fun doA() {
-
- }
- public static final void main() {
- }
-
- // $FF: synthetic method
- public static void main(String[] var0) {
- main();
- }
- @Nullable
- public static final Object doA(@NotNull Continuation $completion) {
- return Unit.INSTANCE;
- }
被调用的挂起函数需要传入一个Continuation, 没有被suspend修饰的函数是没有Continuation参数的,所以没法在普通函数中调用挂起函数,普通函数没有Continuation。
挂起函数最终都是在协程中被调用,协程提供了挂起函数运行的环境。