關於Android LifecycleScope的一點點發現

語言: CN / TW / HK

例子

image-20220119150748335

代碼的運行順序應該是one(),two()還是two(),one(),或者是兩個調用的時機是隨機的,看協程的調度呢?相信很多人都會選擇最後一個.但實際無論運行多少次都是下面的結果

image-20220119151317244

並且launch()方法傳入的suspend 函數甚至會阻塞主線程,怎麼回事兒,這個協程是假的協程?嗎!

先説結論

Kotlin的協程在不同的平台有不同的實現方式.本文以我最熟悉的Android平台為例.在Android中,啟動一個協程使用Dispatcher.Main.immediate作為調度器的話,如果當前線程是主線程,就會將任務立即在主線程執行,如果是在其它線程中,使用Handler.post()方法分給主線程去執行.

如果是Dispatcher.Main作為調度器,就會直接使用主線程的Handler.post()去執行.

CoroutineDispatcher:協程調度器

image-20220119110709671

image-20220119110813517

在導入了androidx.lifecycle:lifecycle-runtime-ktx依賴後,可以在Activity中使用lifecycleScope.launch{}啟動一個協程,這裏默認的上下文就是 EmptyCoroutineContext,因此啟動的協程上下文就是

kotlin lifecycleScope.coroutineContext+EmptyCoroutineContext

看下lifecycleScope是怎麼來的

image-20220119145648128

這樣lifecycleScope.launch{}這樣啟動一個協程默認的調度器就是Dispatcher.Main.immediate

Dispatcher顧名思義就是調度器,在這裏就是負責協程的調度功能,導入依賴查看Android平台的Disptcher.Main的最終實現是由HandlerContext實現的

image-20220119115307629

HandlerContext重寫了isDispatchNeeded方法

image-20220119115553319

查看isDispatchNeeded的註釋可知道如果該方法返回true就會通過dispatch()將協程分發出去,false就會令協程在當前線程恢復,因此Disptcher.Main會始終調用dispather()來進行分發,而Dispatcher.Main.immediate只有在非主線程中會調用dispatch().何時會調用CoroutineDispatcher.isDispatchNeeded()方法呢?通過查看函數調用在DispatchedContinuation.resumeWith()方法中使用到了它.

DispatchedContinuation:協程調度的發起者

這裏只關注DispatchedContinuation是如何讓協程立即在當前線程恢復的邏輯

image-20220119142039471

image-20220119143447268

executeUnconfined()方法是DispatchedContinuation的一個拓展函數,協程的執行是交給EventLoop處理,它類似Handler的MessageQueue,如果當前線程的隊列已滿就會使用EventLoop.dispatchUnconfined()將這個協程調度到當前的EventLoop中,否則直接調用runUnconfinedEventLoop()將協程執行