Hello Coroutine
什么是协程
- 起源
协程作用(个人见解)
-
提高cpu使用效率
-
io密集型
- 在io时候大多是磁盘的io读写操作,会有等待,导致cpu空闲,被占有但未被使用
-
cpu密集型
- 协程对此无作用
-
使用kotlin协程验证
-
方案
- 开启多个协程 充分执行CPU任务,验证 : 结果 一个协程对应一个线程,则协程未提高cpu使用效率
- 开启多个协程 充分执行执行等待操作的任务,验证 : 结果多个协程都在同一个线程里执行,则协程能提高cpu使用效率
-
sample
fun main(args: Array<String>) { // sample1() runBlocking { launch(Dispatchers.IO) { val i = 0 while (true) { println("${Thread.currentThread().name} ====launch1==>$i") } } launch(Dispatchers.IO) { val i = 0 while (true) { println("${Thread.currentThread().name} ====launch2==>$i") } } } }
-
-
Kotlin中的协程
-
cps(continuation-passing style )
- CPS把函数调用完之后接下来要执行的代码通过闭包包裹并作为函数参数调用要执行的函数。
- java中的回调,oc的block块,js中的闭包
-
简单例子
- hello world
import kotlinx.coroutines.* fun main() { hello3() } fun hello3() { runBlocking { launch { println("hello world!!") } } }
-
演进
-
CoroutineScope作用域
-
public interface CoroutineScope { public val coroutineContext: CoroutineContext }
-
管理作用域下的所有协程
- scope.cancel()可以取消所有正在执行的协程
- 常用的CoroutineScope ,ViewModel 有 viewModelScope,Lifecycle 有 lifecycleScope、mainScope
-
-
CoroutineContext(链表数据结构的接口)常用 EmptyCoroutineContext,CombinedContext等,
@SinceKotlin("1.3") public interface CoroutineContext { public operator fun <E : Element> get(key: Key<E>): E? public fun <R> fold(initial: R, operation: (R, Element) -> R): R public operator fun plus(context: CoroutineContext): CoroutineContext = if (context === EmptyCoroutineContext) this else context.fold(this) { acc, element -> val removed = acc.minusKey(element.key) if (removed === EmptyCoroutineContext) element else { val interceptor = removed[ContinuationInterceptor] if (interceptor == null) CombinedContext(removed, element) else { val left = removed.minusKey(ContinuationInterceptor) if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else CombinedContext(CombinedContext(left, element), interceptor) } } } public fun minusKey(key: Key<*>): CoroutineContext public interface Key<E : Element> public interface Element : CoroutineContext { public val key: Key<*> public override operator fun <E : Element> get(key: Key<E>): E? = @Suppress("UNCHECKED_CAST") if (this.key == key) this as E else null public override fun <R> fold(initial: R, operation: (R, Element) -> R): R = operation(initial, this) public override fun minusKey(key: Key<*>): CoroutineContext = if (this.key == key) EmptyCoroutineContext else this } }
-
- CoroutineContext使用以下元素集定义协程的行为: Job:控制协程的声明周期; CoroutineDispatcher:将任务分派到适当的线程;(Java的线程池) CoroutineName:协程的名称,可用于调试; CoroutineExceptionHandler:处理uncaught exceptions。
- Job 、 Job lifecycle
val job = launch { println("test job") }
- 不使用协程序
class CoroutinesSample { fun login(username:String,pad:String,callback:(token:String)->Unit){ try { TimeUnit.SECONDS.sleep(1) }catch (e:Exception){ e.printStackTrace() } callback("default token") } fun getUserInfo(token:String,callback:(userName:String)->Unit){ try { TimeUnit.SECONDS.sleep(1) }catch (e:Exception){ e.printStackTrace() } callback("user name hgwxr") } }
fun main(args: Array<String>) { val sample = CoroutinesSample() sample.apply { login("hgwxr","psd") {token -> println("received token") getUserInfo(token){userName -> println("received user name: $userName") } } } }
received token:default token received user name: user name hgwxr Process finished with exit code 0
- 协程实现
suspend fun getUserInfo2Coroutines(token: String): String { return suspendCoroutine { getUserInfo(token) { userName -> it.resume(userName) } } } suspend fun login2Coroutines(username: String, psd: String): String { return suspendCoroutine { login(username, psd) { token -> it.resume(token) } } } suspend fun loginCoroutines(username: String, pad: String): String { return coroutineScope { delay(1000L) "default token" } } suspend fun getUserInfoCoroutines(token: String): String { return coroutineScope { delay(1000L) "user name hgwxr" } }
fun main(args: Array<String>) { // val sample = CoroutinesSample() sample.apply { runBlocking { val token = login2Coroutines("hgwxr", "psd") println("received token:$token") val userName = getUserInfo2Coroutines(token) println("received user name: $userName") } } }
-
挂起实现原理
-
suspend
-
suspend fun fun1() { println("test suspend....") }
-
编译成字节码再转成java
-
@Nullable public static final Object fun1(@NotNull Continuation $completion) { String var1 = "test suspend...."; boolean var2 = false; System.out.println(var1); return Unit.INSTANCE; }
-
-
Continuation
-
@SinceKotlin("1.3") public interface Continuation<in T> { public val context: CoroutineContext public fun resumeWith(result: Result<T>) }
-
-
状态机实现挂起和恢复
- 简单挂起协程
suspend fun fun3(): String { return suspendCoroutine<String> { it.resume("hello world") } }
@Nullable public static final Object fun3(@NotNull Continuation $completion) { boolean var1 = false; boolean var2 = false; boolean var3 = false; SafeContinuation var4 = new SafeContinuation(IntrinsicsKt.intercepted($completion)); Continuation it = (Continuation)var4; int var6 = false; String var8 = "hello world"; boolean var9 = false; Companion var10 = Result.Companion; boolean var11 = false; it.resumeWith(Result.constructor-impl(var8)); Object var10000 = var4.getOrThrow(); if (var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) { DebugProbesKt.probeCoroutineSuspended($completion); } return var10000; }
-
public static final void main(@NotNull String[] args) { Intrinsics.checkNotNullParameter(args, "args"); BuildersKt.runBlocking$default((CoroutineContext)null, (Function2)(new Function2((Continuation)null) { int label; @Nullable public final Object invokeSuspend(@NotNull Object $result) { Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); Object var10000; switch(this.label) { case 0: ResultKt.throwOnFailure($result); this.label = 1; var10000 = CoroutinesSample3Kt.fun3(this); if (var10000 == var4) { return var4; } break; case 1: ResultKt.throwOnFailure($result); var10000 = $result; break; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } String res = (String)var10000; boolean var3 = false; System.out.println(res); return Unit.INSTANCE; } @NotNull public final Continuation create(@Nullable Object value, @NotNull Continuation completion) { Intrinsics.checkNotNullParameter(completion, "completion"); Function2 var3 = new <anonymous constructor>(completion); return var3; } public final Object invoke(Object var1, Object var2) { return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE); } }), 1, (Object)null); }
-
Kotlin协程优点
-
应用场景
-
场景一
-
异步任务执行
- 网络请求
- 异步任务
-
View测量会之后获取相关信息
-
view.post { val height = view.measuredHeight val width = view.measuredWidth }
-
suspend fun View.await() = suspendCoroutine<View> { coroutine-> this.post { coroutine.resume(this) } }
-
-
点击或监听事件,权限申请,图片加载(github.com/coil-kt/coi… 等等
-
suspend fun AlertDialog.awaitClick(btnText: String): Boolean { return suspendCoroutine { setButton( DialogInterface.BUTTON_POSITIVE, btnText ) { _, _ -> it.resume(true) } setButton( DialogInterface.BUTTON_NEGATIVE, btnText ) { _, _ -> it.resume(false) } setButton( DialogInterface.BUTTON_NEUTRAL, btnText ) { _, _ -> it.resume(false) } } }
-
-
-
场景二
-
同时有多网络请求或多异步任务,都有结果后再处理
-
launch { mIView?.showLoadingBackupUI() val lyfyRecordItem = async { fetchRecordAppInfosWithCoroutine(APP_NAME_LYFY, APP_CODE_LYFY) } val yjfyRecordItem = async { fetchRecordAppInfosWithCoroutine(APP_NAME_YJFY, APP_CODE_YJFY) } val tszmRecordItem = async { fetchRecordAppInfosWithCoroutine(APP_NAME_TSZM, APP_CODE_TSZM) } val fetchList = mutableListOf<RecordItem>().apply { add(lyfyRecordItem.await()) add(yjfyRecordItem.await()) add(tszmRecordItem.await()) } }
-
-
-
场景三(可以不展开讲 主要用Flow channel 类比 Rxjava)
- 数据流 Flow
sealed class P { data class OK(val data: String) : P() data class Process(val process: Int) : P() data class Error(val errorCode: Int, val msg: String) : P() data class Complete(val msg: String) : P() } fun getFlow(): Flow<P> { return flow<P> { emit(P.OK("hgwxr")) delay(1000) emit(P.Process(1)) delay(1000) emit(P.Process(10)) delay(1000) emit(P.Process(100)) delay(50) emit(P.Error(-1, "erorr msg")) emit(P.Complete("complete")) }.flowOn(Dispatchers.IO) } fun main(args: Array<String>) { runBlocking { getFlow().collect { println("Thread name :${Thread.currentThread().name} value :$it") } } }
- 同一个接口多次回调返回数据(下载,翻译,socket)
- 使用callbackFlow
- 多异步任务相互关联 类似于rxjava的 flatmap
其他知识点
异常处理,异常取消、超时,mutext,actor,channel等