Kotlin協(xié)程基礎(chǔ)元素梳理分析
Kotlin 協(xié)程的基礎(chǔ)元素:Continuation、SafeContinuation、CoroutineContext、CombinedContext、CancellationException、intrinsics。CombinedContext是 CoroutineContext 的一個(gè)實(shí)現(xiàn)類,SafeContinuation是 Continuation 的實(shí)現(xiàn)類。
Continuation 是什么?
class Call<T>(callBack: Call.CallBack<T>) { fun cancel() { } interface CallBack<T> { fun onSuccess(data: T) fun onFail(throwable: Throwable) } } suspend fun <T : Any> Call<T>.await(): T = suspendCancellableCoroutine<T> { continuation -> val call = Call(object : Call.CallBack<T> { override fun onSuccess(data: T) { continuation.resume(data, onCancellation = null) } override fun onFail(throwable: Throwable) { continuation.resumeWithException(throwable) } }) continuation.invokeOnCancellation { call.cancel() } }
要實(shí)現(xiàn)掛起函數(shù)的時(shí)候,可以使用 suspendCoroutine{}、suspendCancellableCoroutine{}這兩個(gè)高階函數(shù),在它們的 Lambda 當(dāng)中,我們可以使用它暴露出來(lái)的 continuation 對(duì)象,把程序的執(zhí)行結(jié)果或異常傳到外部去。常用于實(shí)現(xiàn)掛起函數(shù)內(nèi)部邏輯。
fun checkLength() { runBlocking { val result = getStrLengthSuspend("Kotlin") println(result) } } suspend fun getStrLengthSuspend(text:String):Int = suspendCoroutine { continuation -> thread { Thread.sleep(1000L) continuation.resume(text.length) } } Log 6 Process finished with exit code 0
使用 suspendCoroutine{}實(shí)現(xiàn)了掛起函數(shù),在它內(nèi)部,使用 continuation.resume() 的方式,傳出了掛起函數(shù)的返回值。
為什么以 continuation.resume() 這樣異步的方式傳出結(jié)果,掛起函數(shù)就能接收到結(jié)果呢?
suspend fun getStrLengthSuspend(text: String): Int = suspendCoroutine { continuation -> thread { Thread.sleep(1000L) continuation.resume(text.length) } } fun checkLength2() { val func = ::getStrLengthSuspend as (String, Continuation<Int>) -> Any? func("Kotlin", object :Continuation<Int> { override val context: CoroutineContext get() = EmptyCoroutineContext override fun resumeWith(result: Result<Int>) { println(result.getOrNull()) } }) Thread.sleep(2000L) } Log 6 Process finished with exit code 0
把函數(shù)強(qiáng)轉(zhuǎn)成了帶有 Continuation 的函數(shù)類型,然后通過匿名內(nèi)部類的方式,創(chuàng)建了一個(gè) Continuation 對(duì)象傳了進(jìn)去。掛起函數(shù)的本質(zhì),就是 Callback!
把 Continuation 改為 Callback:
fun getStrLength() { func2("Kotlin", object : MyCallBack<Int> { override fun resume(value: Int) { println(value) } }) } fun func2(text: String, callBack: MyCallBack<Int>) { thread { Thread.sleep(1000L) callBack.resume(text.length) } } interface MyCallBack<T> { fun resume(value: T) } Log 6 Process finished with exit code 0
Continuation 改成 Callback 以后,使用匿名內(nèi)部類創(chuàng)建 Callback 用于接收異步結(jié)果;異步函數(shù)內(nèi)部,使用 callback.resume() 將結(jié)果傳出去。
Kotlin 協(xié)程當(dāng)中的 Continuation,作用其實(shí)就相當(dāng)于 Callback,它既可以用于實(shí)現(xiàn)掛起函數(shù),往掛起函數(shù)的外部傳遞結(jié)果;也可以用于調(diào)用掛起函數(shù),我們可以創(chuàng)建 Continuation 的匿名內(nèi)部類,來(lái)接收掛起函數(shù)傳遞出來(lái)的結(jié)果。
Java 代碼中如何調(diào)用 Kotlin 的掛起函數(shù)嗎?
public static void main(String[] args) { SuspendFromJavaDo.INSTANCE.getUserInfo(new Continuation<String>() { @NonNull @Override public CoroutineContext getContext() { return EmptyCoroutineContext.INSTANCE; } @Override public void resumeWith(@NonNull Object o) { System.out.println(o + ""); } }); } object SuspendFromJavaDo { suspend fun getUserInfo():String { delay(1000L) return "Kotlin" } }
所以,實(shí)現(xiàn)掛起函數(shù)邏輯的時(shí)候,常用到 suspendCoroutine{}、suspendCancellableCoroutine{}。
Continuation.kt源碼
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>) }
public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return suspendCoroutineUninterceptedOrReturn { c: Continuation<T> -> val safe = SafeContinuation(c.intercepted()) block(safe) safe.getOrThrow() } }
suspendCoroutineUninterceptedOrReturn{}實(shí)現(xiàn)了suspendCoroutine{}。
val safe = SafeContinuation(c.intercepted()) 包裹Continuation。
block(safe)調(diào)用 Lambda 當(dāng)中的邏輯。
safe.getOrThrow() 取出 block(safe) 的運(yùn)行結(jié)果,Continuation 當(dāng)中是可以存儲(chǔ) result 的。這個(gè) Result 可能是正確的結(jié)果,也可能是異常。
@Suppress("UNUSED_PARAMETER", "RedundantSuspendModifier") public suspend inline fun <T> suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation<T>) -> Any?): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } throw NotImplementedError("Implementation of suspendCoroutineUninterceptedOrReturn is intrinsic") }
為什么這個(gè)高階函數(shù)的源代碼會(huì)是拋出異常呢?
“Implementation of suspendCoroutineUninterceptedOrReturn is intrinsic.”suspendCoroutineUninterceptedOrReturn 是一個(gè)編譯器內(nèi)建函數(shù),它是由 Kotlin 編譯器來(lái)實(shí)現(xiàn)的。
suspendCoroutineUninterceptedOrReturn 這個(gè)高階函數(shù)的參數(shù),它會(huì)接收一個(gè) Lambda,類型是(Continuation<T>) -> Any?,這里的“Any?”類型,其實(shí)就能代表當(dāng)前這個(gè)掛起函數(shù)是否真正掛起。
fun test() { runBlocking { val result = testNoSuspendCoroutinue() println(result) } } private suspend fun testNoSuspendCoroutinue() = suspendCoroutineUninterceptedOrReturn<String>{ continuation -> return@suspendCoroutineUninterceptedOrReturn "Kotlin" } Log Kotlin Process finished with exit code 0
直接使用 suspendCoroutineUninterceptedOrReturn 實(shí)現(xiàn)了掛起函數(shù),并且,在它的 Lambda 當(dāng)中,我們并沒有調(diào)用 continuation.resume()。在掛起函數(shù)的外部確實(shí)也可以接收到這個(gè)結(jié)果。
private static final Object testNoSuspendCoroutinue(Continuation $completion) { int var2 = false; if ("Kotlin" == IntrinsicsKt.getCOROUTINE_SUSPENDED()) { DebugProbesKt.probeCoroutineSuspended($completion); } return "Kotlin"; }
這個(gè)函數(shù)其實(shí)就是一個(gè)偽掛起函數(shù),它的內(nèi)部并不會(huì)真正掛起。這樣,當(dāng)我們從外部調(diào)用這個(gè)函數(shù)的時(shí)候,這個(gè)函數(shù)會(huì)立即返回結(jié)果。
private suspend fun testSuspendCoroutinues() = suspendCoroutineUninterceptedOrReturn<String> { continuation -> thread { Thread.sleep(2000L) continuation.resume("Kotlin") } return@suspendCoroutineUninterceptedOrReturn kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED } fun main() { runBlocking { val testSuspendCoroutinues = testSuspendCoroutinues() println(testSuspendCoroutinues) } } Kotlin Process finished with exit code 0
使用了 continuation.resume(),掛起函數(shù)的外部也能接收到這個(gè)結(jié)果。
private static final Object testSuspendCoroutinues(Continuation $completion) { int var2 = false; ThreadsKt.thread$default(false, false, (ClassLoader)null, (String)null, 0, (Function0)(new TestCoroutine777Kt$testSuspendCoroutinues$2$1($completion)), 31, (Object)null); Object var10000 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); //將 var10000 賦值為 COROUTINE_SUSPENDED 這個(gè)掛起標(biāo)志位。 if (var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) { DebugProbesKt.probeCoroutineSuspended($completion); } //返回掛起標(biāo)志位,代表 testSuspendCoroutinues() 這個(gè)函數(shù)會(huì)真正掛起。 return var10000; } BuildersKt.runBlocking$default((CoroutineContext)null, (Function2)(new Function2((Continuation)null) { int label; @Nullable public final Object invokeSuspend(@NotNull Object $result) { Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); Object var10000; switch(this.label) { case 0: ResultKt.throwOnFailure($result); this.label = 1; var10000 = TestCoroutine777Kt.testSuspendCoroutinues(this); if (var10000 == var3) { return var3; } break; case 1: ResultKt.throwOnFailure($result); var10000 = $result; break; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } String testSuspendCoroutinues = (String)var10000; System.out.println(testSuspendCoroutinues); 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);
由于 suspend 修飾的函數(shù),既可能返回 CoroutineSingletons.COROUTINE_SUSPENDED,也可能返回實(shí)際結(jié)果,甚至可能返回 null,為了適配所有的可能性,CPS 轉(zhuǎn)換后的函數(shù)返回值類型就只能是 Any? 了。
suspendCoroutineUninterceptedOrReturn{}這個(gè)高階函數(shù)的作用了:它可以將掛起函數(shù)當(dāng)中的 Continuation 以參數(shù)的形式暴露出來(lái),在它的 Lambda 當(dāng)中,我們可以直接返回結(jié)果,這時(shí)候它就是一個(gè)“偽掛起函數(shù)”;或者,我們也可以返回 COROUTINE_SUSPENDED 這個(gè)掛起標(biāo)志位,然后使用 continuation.resume() 傳遞結(jié)果。相應(yīng)的,suspendCoroutine{}、suspendCancellableCoroutine{}這兩個(gè)高階函數(shù),只是對(duì)它的一種封裝而已。
到此這篇關(guān)于Kotlin協(xié)程基礎(chǔ)元素梳理分析的文章就介紹到這了,更多相關(guān)Kotlin協(xié)程基礎(chǔ)元素內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android LeakCanary檢測(cè)內(nèi)存泄露原理
這篇文章主要介紹了分析LeakCanary檢測(cè)內(nèi)存泄露原理,幫助大家更好的理解和學(xué)習(xí)使用Android開發(fā),感興趣的朋友可以了解下2021-03-03Android利用WindowManager生成懸浮按鈕及懸浮菜單
這篇文章主要為大家詳細(xì)介紹了Android利用WindowManager生成懸浮按鈕及懸浮菜單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01Android編程使用ListView實(shí)現(xiàn)數(shù)據(jù)列表顯示的方法
這篇文章主要介紹了Android編程使用ListView實(shí)現(xiàn)數(shù)據(jù)列表顯示的方法,實(shí)例分析了Android中ListView控件的使用技巧,需要的朋友可以參考下2016-01-01Android AIDL實(shí)現(xiàn)跨進(jìn)程通信的示例代碼
本篇文章主要介紹了Android AIDL實(shí)現(xiàn)跨進(jìn)程通信的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08Android實(shí)現(xiàn)拖動(dòng)效果的兩種方法
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)拖動(dòng)效果的兩種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04Android模擬實(shí)現(xiàn)滑動(dòng)解鎖界面
這篇文章主要為大家詳細(xì)介紹了Android模擬實(shí)現(xiàn)滑動(dòng)解鎖界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01基于Vert.x和RxJava 2構(gòu)建通用的爬蟲框架的示例
這篇文章主要介紹了基于Vert.x和RxJava 2構(gòu)建通用的爬蟲框架的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2018-02-02