網站首頁 編程語言 正文
正文
launch我們經常用,今天來看看它是什么原理。
建議: 食用本篇文章之前記得先食用Kotlin協程之createCoroutine和startCoroutine
launch使用
launch我們應該很熟悉了,隨便舉個例子:
fun main() { val coroutineScope = CoroutineScope(Job()) coroutineScope.launch { println("1969年 葉文潔進入紅岸基地") println("1971年 紅岸基地,葉文潔第一次向太陽發送信號,但未發現回波") delay(4000L) println("1975年 半人馬座三星,三體世界得知地球存在") } Thread.sleep(5000L) }
sleep(5000L)和launch內部是在2個線程中,互不干涉
簡單地使用launch配合delay輸出了幾條語句。為了了解它底層的實現原理,還是老規矩,先反編譯一下。
public final class LaunchTestKt { public static final void main() { Job unused = BuildersKt__Builders_commonKt.launch$default(CoroutineScopeKt.CoroutineScope(JobKt.Job$default((Job) null, 1, (Object) null)), (CoroutineContext) null, (CoroutineStart) null, new LaunchTestKt$main$1((Continuation<? super LaunchTestKt$main$1>) null), 3, (Object) null); Thread.sleep(5000); } } final class LaunchTestKt$main$1 extends SuspendLambda implements Function2<CoroutineScope, Continuation<? super Unit>, Object> { int label; LaunchTestKt$main$1(Continuation<? super LaunchTestKt$main$1> continuation) { super(2, continuation); } public final Continuation<Unit> create(Object obj, Continuation<?> continuation) { return new LaunchTestKt$main$1(continuation); } public final Object invoke(CoroutineScope coroutineScope, Continuation<? super Unit> continuation) { return ((LaunchTestKt$main$1) create(coroutineScope, continuation)).invokeSuspend(Unit.INSTANCE); } public final Object invokeSuspend(Object $result) { Object coroutine_suspended = IntrinsicsKt.getCOROUTINE_SUSPENDED(); switch (this.label) { case 0: ResultKt.throwOnFailure($result); System.out.println("1969年 葉文潔進入紅岸基地"); System.out.println("1971年 紅岸基地,葉文潔第一次向太陽發送信號,但未發現回波"); this.label = 1; if (DelayKt.delay(4000, this) != coroutine_suspended) { break; } else { return coroutine_suspended; } case 1: ResultKt.throwOnFailure($result); break; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } System.out.println("1975年 半人馬座三星,三體世界得知地球存在"); return Unit.INSTANCE; } }
ps:上面這段代碼是通過jadx反編譯apk的方式拿到的源碼,看起來更加人性化。具體的流程是我們用Android Studio寫個掛起函數的demo,然后編譯成apk,然后將apk用jadx反編譯一下,拿到對應class的反編譯Java源碼,這樣弄出來的源碼我感覺比直接通過Android Studio的Tools->Kotlin->Show Kotlin拿到的源碼稍微好看懂一些。
咦,LaunchTestKt$main$1
有沒有很眼熟?這不就是前面我們分析startCoroutine原理時得到的匿名內部類么,簡直一模一樣。這個LaunchTestKt$main$1類對應的是launch的Lambda塊,它本質上是一個Continuation。
startCoroutine原理
LaunchTestKt$main$1
相關的原理,在前面已經分析過了,這里不再贅述。這里主要看一下launch是如何與這個LaunchTestKt$main$1
進行關聯的。
launch原理
launch函數如下:
public fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit ): Job { //代碼1 val newContext = newCoroutineContext(context) //代碼2 val coroutine = if (start.isLazy) LazyStandaloneCoroutine(newContext, block) else StandaloneCoroutine(newContext, active = true) //代碼3 coroutine.start(start, coroutine, block) return coroutine }
- 將傳入的CoroutineContext構造出新的context
- 啟動模式,判斷是否為懶加載,如果是懶加載則構建懶加載協程對象,否則就是標準的
- 啟動協程
context相關的先不看,因為我們demo這里不是懶加載的所以創建出來的是StandaloneCoroutine,直接看一下start是怎么啟動協程的。
private open class StandaloneCoroutine( parentContext: CoroutineContext, active: Boolean ) : AbstractCoroutine<Unit>(parentContext, initParentJob = true, active = active) { override fun handleJobException(exception: Throwable): Boolean { handleCoroutineException(context, exception) return true } } public abstract class AbstractCoroutine<in T>( parentContext: CoroutineContext, initParentJob: Boolean, active: Boolean ) : JobSupport(active), Job, Continuation<T>, CoroutineScope { public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) { start(block, receiver, this) } }
start函數是在父類AbstractCoroutine中實現的,這個start函數里面又調用了一個新的start函數,當我們點擊這個里面的start函數想進去看源碼時發現,點不過去,點了之后還是在當前位置..... ???啥情況
CoroutineStart中找invoke方法
仔細觀察發現,start是一個CoroutineStart對象,直接使用CoroutineStart對象然后后面就接括號了,這是類里面有定義operator invoke方法,然后Kotlin可以通過這種方式來簡化調用。我們直接去CoroutineStart中找invoke方法:
public enum class CoroutineStart { DEFAULT, LAZY, ATOMIC, UNDISPATCHED; public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit = when (this) { DEFAULT -> block.startCoroutineCancellable(receiver, completion) ATOMIC -> block.startCoroutine(receiver, completion) UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion) LAZY -> Unit // will start lazily } public val isLazy: Boolean get() = this === LAZY }
CoroutineStart是個枚舉類,定義了協程的幾種啟動方式:DEFAULT、LAZY、ATOMIC、UNDISPATCHED。在invoke函數中,根據當前是哪種啟動方式進行開啟協程。
當如果使用ATOMIC的方式,也就是不可取消的協程,就觸發了block.startCoroutine(receiver, completion)
。有沒有覺得很眼熟,它其實就是我們上節課中分析的啟動協程的關鍵:startCoroutine。
demo中使用的是默認的方式,也就是DEFAULT,它只不過是在ATOMIC的基礎上,對startCoroutine包裝了一下,使其成為可響應取消的協程。而UNDISPATCHED的方式,也就是不分發到其他線程去執行,而是直接在當前線程中進行執行。
startCoroutineCancellable邏輯
來看下DEFAULT之后走的startCoroutineCancellable邏輯:
public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>): Unit = runSafely(completion) { createCoroutineUnintercepted(completion).intercepted().resumeCancellableWith(Result.success(Unit)) } public actual fun <T> (suspend () -> T).createCoroutineUnintercepted( completion: Continuation<T> ): Continuation<Unit> { val probeCompletion = probeCoroutineCreated(completion) return if (this is BaseContinuationImpl) //走這里 create(probeCompletion) else createCoroutineFromSuspendFunction(probeCompletion) { (this as Function1<Continuation<T>, Any?>).invoke(it) } }
這塊就是前面文章中分析的代碼了,launch就算是走完了。其本質上是對startCoroutine()這個基礎API進行了一些封裝,讓開發者更方便使用。
小結
launch、async之類的是Kotlin協程框架中的中間層,它們是協程構建器。而在協程構建器的內部,實際上是對協程基礎API:?createCoroutine{}
、startCoroutine{}
的封裝。它們除了擁有啟動協程的基礎能力,還支持傳入CoroutineContext(結構化并發)、CoroutineStart(啟動模式) 等參數,方便開發者使用
原文鏈接:https://github.com/xfhy/Android-Notes/blob/master/Blogs/Kotlin/4.Kotlin
相關推薦
- 2022-07-10 Callable接口的使用詳解
- 2023-12-24 npm安裝element ui出錯的問題--版本不匹配
- 2022-12-04 C++?Boost?Graph算法超詳細精講_C 語言
- 2022-10-23 Android?手寫RecyclerView實現列表加載_Android
- 2022-03-03 text-overflow:ellipsis,當對象內文本溢出時顯示省略標記(...)
- 2022-10-16 Python計算標準差之numpy.std和torch.std的區別_python
- 2022-09-19 Android實現多張圖片合成加載動畫_Android
- 2022-05-14 .NetCore?Web?Api?利用ActionFilterAttribute統一接口返回值格式及
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支