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挂起函数整理-基础】挂起函数最终都是在协程中被调用,协程提供了挂起函数运行的环境。
推荐阅读
- Kotlin|【Kotlin基础系列】第2章 基本语法(1)
- 快讯|华为Mate 50将于9月6日发布;马斯克薪酬高达100亿美元;Kotlin成为Android官方支持语言5周年|极客头条
- Kotlin|浅谈Android dataBinding使用
- Kotlin实战指南|Kotlin密封类sealed
- android|Kotlin Sealed Class
- Jetpack|深入理解 Jetpack Compose 内核(SlotTable 系统)
- java|MVVM框架第二篇 Navigation
- OpenHarmony Sensor 模块Callback注册和回调全流程
- #yyds干货盘点# Kotlin随查指南,妈妈再也不担心我不会Ctrl+F了