相关链接
协程是什么
并发设计模式,可以简化异步执行的代码
协程能做什么
协程可以用来执行异步任务
和线程有什么区别,看扔物线的课程说,kotlin 协程就是线程,而线程意味者 CPU 资源, cpu 的时间片
怎么使用
launch({ val user = api.getUser() // 👈 网络请求(IO 线程) nameTv.text = user.name // 👈 更新 UI(主线程) })
launch 函数加上实现在 {} 中具体的逻辑,就构成了一个协程。
-
挂起函数是什么
协程中的一种函数,在执行到 suspend 函数时,主线程暂停执行协程中的剩余代码
那函数挂起以后呢,谁来执行呢? 由挂起函数指定,一般都需要指定子线程(IO),不然挂起没有意义
也就是协程可以用来做异步任务并且不用写很多回调了
-
Suspend 修饰
挂起函数的使用也有一些限制,比如只能在协程中或者其他挂起函数中使用
挂起和恢复
- 挂起是函数的挂起,相当于 return
- 恢复也是函数的恢复,相当于 callback
当使用 suspend 声明一个挂起函数时,有点类似注解,告诉编译器需要实现一些代码,从协程的源码中是很难了解到它的原理的,需要观察编译后的产物
示例查看的步骤:
kotlin
suspend fun testCoroutine(){
Log.e("打印","测试 delay 前")
delay(2000)
Log.e("打印","测试 delay后打印")
}
编译后的结果
php
@Nullable
public final Object testCoroutine(@NotNull Continuation var1) {
Object $continuation;
label20: {
if (var1 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var1;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
$continuation = new ContinuationImpl(var1) {
// $FF: synthetic field
Object result;
int label;
// 唤醒 - continuation 回调执行 invokeSuspend
// 内部执行了我们的 testCoroutine()
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return MainActivity.this.testCoroutine(this);
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
Log.e("打印", "测试 delay 前");
((<undefinedtype>)$continuation).label = 1;
// 这里挂起就是直接 return 了
// var4 就是 COROUTINE_SUSPENDED
if (DelayKt.delay(2000L, (Continuation)$continuation) == var4) {
return var4;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
Log.e("打印", "测试 delay后打印");
return Unit.INSTANCE;
}
怎样改造原有代码
落到应用上,怎样使用协程来改造我们的旧项目代码呢?
- 方式一
java
/** 举例
** fun parseFile()
**/
lifecyclesScope.launch{
val fileContent = lifecyclesScope.async{
parseFile().await()
}
println(fileContent)
}
这种方式通过调用 await() 来等待方法执行结束,不过每次都要自行再包一层 async{},并且还要带要调用一下 await() 有点不太优雅
这里补充一下,async 会开启线程来执行任务,内部的线程池的核心线程数为 ==2== ,最大线程数为 ==cpu 核心数==
- 方式二
kotlin
lifecyclesScope.launch{
val fileContent = parseFile()
println(fileContent)
}
/\*\*
* 改造 parseFile 内部
\*\*/
suspend fun parseFile(){
// 编译以后就会变成 CoroutineSuspended
// 执行会挂起
return suspendCanceableCoroutine{ continuation->
Thread{
//... io
continuation.resumeWith(Result.success("xxxxx"))
}.start()
}
}
// 这种方式类似 delay() 的实现
public suspend fun delay(timeMillis: Long) {
if (timeMillis <= 0) return // don't delay
return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
// if timeMillis == Long.MAX\_VALUE then just wait forever like awaitCancellation, don't schedule.
if (timeMillis < Long.MAX\_VALUE) {
cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
}
}
}
这种方式是通过使用 suspendCancellableCoroutine 包裹我们原有的代码块,在执行完成后 resumeWith 返回结果
Retrofit 在 2.8 以后,通过扩展 Call 支持 await() ,内部也是用 suspendCancellableCoroutine 来实现的