Kotlin|Kotlin挂起函数整理-基础

1.Kotlin 协程的优势

  • 解决回调地狱的问题。
  • 以同步的方式完成异步任务。

示例:
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 { /** * 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) }

注意:挂起函数,只能在协程中被调用,或者被其他挂起函数调用。


为什么挂起函数可以调用挂起函数,而普通函数不能调用挂起函数?
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。
【Kotlin|Kotlin挂起函数整理-基础】挂起函数最终都是在协程中被调用,协程提供了挂起函数运行的环境。

    推荐阅读