<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Kotlin 協程的基礎元素:Continuation、SafeContinuation、CoroutineContext、CombinedContext、CancellationException、intrinsics。CombinedContext是 CoroutineContext 的一個實現類,SafeContinuation是 Continuation 的實現類。
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() } }
要實現掛起函數的時候,可以使用 suspendCoroutine{}、suspendCancellableCoroutine{}這兩個高階函數,在它們的 Lambda 當中,我們可以使用它暴露出來的 continuation 物件,把程式的執行結果或異常傳到外部去。常用於實現掛起函數內部邏輯。
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{}實現了掛起函數,在它內部,使用 continuation.resume() 的方式,傳出了掛起函數的返回值。
為什麼以 continuation.resume() 這樣非同步的方式傳出結果,掛起函數就能接收到結果呢?
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
把函數強轉成了帶有 Continuation 的函數型別,然後通過匿名內部類的方式,建立了一個 Continuation 物件傳了進去。掛起函數的本質,就是 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 以後,使用匿名內部類建立 Callback 用於接收非同步結果;非同步函數內部,使用 callback.resume() 將結果傳出去。
Kotlin 協程當中的 Continuation,作用其實就相當於 Callback,它既可以用於實現掛起函數,往掛起函數的外部傳遞結果;也可以用於呼叫掛起函數,我們可以建立 Continuation 的匿名內部類,來接收掛起函數傳遞出來的結果。
Java 程式碼中如何呼叫 Kotlin 的掛起函數嗎?
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" } }
所以,實現掛起函數邏輯的時候,常用到 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{}實現了suspendCoroutine{}。
val safe = SafeContinuation(c.intercepted()) 包裹Continuation。
block(safe)呼叫 Lambda 當中的邏輯。
safe.getOrThrow() 取出 block(safe) 的執行結果,Continuation 當中是可以儲存 result 的。這個 Result 可能是正確的結果,也可能是異常。
@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") }
為什麼這個高階函數的原始碼會是丟擲異常呢?
“Implementation of suspendCoroutineUninterceptedOrReturn is intrinsic.”suspendCoroutineUninterceptedOrReturn 是一個編譯器內建函數,它是由 Kotlin 編譯器來實現的。
suspendCoroutineUninterceptedOrReturn 這個高階函數的引數,它會接收一個 Lambda,型別是(Continuation<T>) -> Any?,這裡的“Any?”型別,其實就能代表當前這個掛起函數是否真正掛起。
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 實現了掛起函數,並且,在它的 Lambda 當中,我們並沒有呼叫 continuation.resume()。在掛起函數的外部確實也可以接收到這個結果。
private static final Object testNoSuspendCoroutinue(Continuation $completion) { int var2 = false; if ("Kotlin" == IntrinsicsKt.getCOROUTINE_SUSPENDED()) { DebugProbesKt.probeCoroutineSuspended($completion); } return "Kotlin"; }
這個函數其實就是一個偽掛起函數,它的內部並不會真正掛起。這樣,當我們從外部呼叫這個函數的時候,這個函數會立即返回結果。
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(),掛起函數的外部也能接收到這個結果。
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 這個掛起標誌位。 if (var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) { DebugProbesKt.probeCoroutineSuspended($completion); } //返回掛起標誌位,代表 testSuspendCoroutinues() 這個函數會真正掛起。 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 修飾的函數,既可能返回 CoroutineSingletons.COROUTINE_SUSPENDED,也可能返回實際結果,甚至可能返回 null,為了適配所有的可能性,CPS 轉換後的函數返回值型別就只能是 Any? 了。
suspendCoroutineUninterceptedOrReturn{}這個高階函數的作用了:它可以將掛起函數當中的 Continuation 以引數的形式暴露出來,在它的 Lambda 當中,我們可以直接返回結果,這時候它就是一個“偽掛起函數”;或者,我們也可以返回 COROUTINE_SUSPENDED 這個掛起標誌位,然後使用 continuation.resume() 傳遞結果。相應的,suspendCoroutine{}、suspendCancellableCoroutine{}這兩個高階函數,只是對它的一種封裝而已。
到此這篇關於Kotlin協程基礎元素梳理分析的文章就介紹到這了,更多相關Kotlin協程基礎元素內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45