揭開Kotlin協程的神秘面紗
Kotlin協程提供了一種新的非同步執行方式,但直接檢視庫函式可能會有點混亂,本文中嘗試揭開協程的神秘面紗。
讓我們從基礎開始吧,假設有一個名為launch可以用來啟動協程
上面的程式碼是使用launch一種非常簡單的方法,返回Job一個非同步執行函式,Job代表一個協程coroutine作業,可以取消或查詢它的狀態。
現在,如果檢視我們的日誌,檢查我們的函式實際執行的是哪個執行緒?我們就會得到類似的結果
E / Thread執行:ForkJoinPool.commonPool-worker-2
我們的程式碼是在一個執行緒中執行的,讓我們稍微瞭解一下launch本身:
再看看DefaultDispatcher的值是什麼?
launch是將CoroutineContext作為第一個引數,這個引數值預設為代表一個CommonPool執行緒池類的DefaultDispatcher,這個執行緒池類根據當前CPU處理器總數建立一個帶有Executors的CoroutineContext。完整程式碼在這裡。
launch是一種協程構建器,可以接受一個協程分配器CoroutineDispatcher,分配器實際上負責在單獨的執行緒中執行程式碼。
我們可以輕鬆建立自己的分配器:
newSingleThreadContext 由Kotlin協同程式庫本身提供,用於建立僅在單個執行緒上執行的上下文。我們可以在此基礎上建立自己的函式:
下面是執行後的日誌
E / Thread執行:singleThreadDispatcher
所以我們用我們自己的執行緒方案建立了我們自己的簡單協程:)
讓我們看看我們可以透過Dispatchers做更多事情:
在這裡,我們建立了三個不同的分配器程式並過載了dispatch方法, 我們在每個dispatch方法中以不同的方式執行Runnable塊,也就是一個簡單的執行緒,這個非同步執行緒是使用RxJava實現,而Android主執行緒是使用Handler完成。
如果我們用這些分配器程式執行我們的函式,我們會得到這些日誌
E / Thread Running:Thread-582
E / Thread Running:RxCachedThreadScheduler-1
E / Thread Running:main
這真的顯示了協同程式的強大功能,因為Coroutines只是語言語法,它們與執行它們的平臺無關。不同執行緒的職責分配只需開發人員使用一組函式就能實現,他可以在Rx執行緒或主執行緒上執行他喜歡的協同程式。
協同程式就像空的冰淇淋甜筒,你可以選擇你想要冰淇淋的填入。
現在因為myHeavyFunction()函式需要很長時間才能執行,所以我們可能想要非同步執行它。
這裡我們將myHeavyFunction()遷移到一個單獨的執行緒並非同步執行它,但是如果我們這樣做:
這裡我們在主執行緒上執行的Coroutine上下文(UI:由coroutine-android庫提供)中執行重量函式,執行仍然是非同步的,因為Coroutines是透過暫停這部分函式處理,但函式執行仍然發生在主執行緒上,而不建立額外的執行緒。
讓我們分析一下:
1. launch(UI)使用Android的UI所在的執行緒上下文建立一個協同Job。
2. 我們透過async非同步建立了另一個協同程式,其中包含我們需要呼叫的函式,唯一的區別是:這個協程返回一個Deferred值,async是協程庫的一部分。
3. 我們呼叫await()函式來捕獲Deferred的未來值。這是在UI所線上程上下文中捕獲的。
總而言之,我們建立了一個非同步執行程式,我們可以在其中傳遞函式並讓它們非同步執行,然後將值返回給UI執行緒。
現在我們在哪裡可以使用它 ? 資料庫查詢
我們將插入到DB的請求變成了一個發射就可以忘記不用等待結果的非同步請求,這是使用singleThreadAsync實現的 。
當我們從DB檢索資料時,我們可以使用我們的asyncExecutor來檢索物件列表,然後使用Collection Framework中的運算子發揮所有kotlin優點啦!
讓我們從基礎開始吧,假設有一個名為launch可以用來啟動協程
private fun myHeavyFunction() { Log.e("Thread Running ", Thread.currentThread().name) } val job = launch { myHeavyFunction() } <p class="indent"> |
上面的程式碼是使用launch一種非常簡單的方法,返回Job一個非同步執行函式,Job代表一個協程coroutine作業,可以取消或查詢它的狀態。
override fun onStop() { if (job.isActive) { job.cancel() } } <p class="indent"> |
現在,如果檢視我們的日誌,檢查我們的函式實際執行的是哪個執行緒?我們就會得到類似的結果
E / Thread執行:ForkJoinPool.commonPool-worker-2
我們的程式碼是在一個執行緒中執行的,讓我們稍微瞭解一下launch本身:
public fun launch( context:CoroutineContext =DefaultDispatcher, start:CoroutineStart CoroutineStart.DEFAULT, parent:Job?=null, onComp1etion:CompletionHand1er? =null, block:suspend CoroutineScope.()->Unit ):Job{ <p class="indent"> |
再看看DefaultDispatcher的值是什麼?
@Suppress("PropertyName ") public actual val DefaultDispatcher: CoroutineDispatcher = CommonPool object CommonPool:CoroutineDispatcher() <p class="indent"> |
launch是將CoroutineContext作為第一個引數,這個引數值預設為代表一個CommonPool執行緒池類的DefaultDispatcher,這個執行緒池類根據當前CPU處理器總數建立一個帶有Executors的CoroutineContext。完整程式碼在這裡。
launch是一種協程構建器,可以接受一個協程分配器CoroutineDispatcher,分配器實際上負責在單獨的執行緒中執行程式碼。
我們可以輕鬆建立自己的分配器:
val singleThreadDispatcher = newSingleThreadContext("singleThreadDispatcher") <p class="indent"> |
newSingleThreadContext 由Kotlin協同程式庫本身提供,用於建立僅在單個執行緒上執行的上下文。我們可以在此基礎上建立自己的函式:
fun <T> singleThreadAsync(block: () -> T): Job = launch(singleThreadDispatcher) { block.invoke() } job = singleThreadAsync { myHeavyFunction() } <p class="indent"> |
下面是執行後的日誌
E / Thread執行:singleThreadDispatcher
所以我們用我們自己的執行緒方案建立了我們自己的簡單協程:)
讓我們看看我們可以透過Dispatchers做更多事情:
object MyDispatcher : CoroutineDispatcher() { override fun dispatch(context: CoroutineContext, block: Runnable) { thread { block.run() } } } object RxDispatcher : CoroutineDispatcher() { override fun dispatch(context: CoroutineContext, block: Runnable) { Observable.fromCallable { block.run() } .subscribeOn(Schedulers.io()) .subscribe {} } } object UIDispatcher : CoroutineDispatcher() { override fun dispatch(context: CoroutineContext, block: Runnable) { Handler(Looper.getMainLooper()).post { block.run() } } } <p class="indent"> |
在這裡,我們建立了三個不同的分配器程式並過載了dispatch方法, 我們在每個dispatch方法中以不同的方式執行Runnable塊,也就是一個簡單的執行緒,這個非同步執行緒是使用RxJava實現,而Android主執行緒是使用Handler完成。
如果我們用這些分配器程式執行我們的函式,我們會得到這些日誌
E / Thread Running:Thread-582
E / Thread Running:RxCachedThreadScheduler-1
E / Thread Running:main
這真的顯示了協同程式的強大功能,因為Coroutines只是語言語法,它們與執行它們的平臺無關。不同執行緒的職責分配只需開發人員使用一組函式就能實現,他可以在Rx執行緒或主執行緒上執行他喜歡的協同程式。
協同程式就像空的冰淇淋甜筒,你可以選擇你想要冰淇淋的填入。
無執行緒Thread-less非同步
編寫非同步程式碼傳統上被認為是一種執行緒工作,其實並不總是如此,讓我們看看如何使用Coroutines解決這個問題
讓我們看看一系列函式執行
mySmallFunction1() myHeavyFunction() // Takes 3 seconds to execute mySmallFunction2() //Order執行順序 E/mySmallFunction1 running on: main E/myHeavyFunction running on: main E/mySmallFunction2 running on: main <p class="indent"> |
現在因為myHeavyFunction()函式需要很長時間才能執行,所以我們可能想要非同步執行它。
mySmallFunction1() thread { myHeavyFunction() } //Execution in a separate thread. mySmallFunction2() //Order順序 E/mySmallFunction1 running on: main E/mySmallFunction2 running on: main E/myHeavyFunction running on: Thread-697 <p class="indent"> |
這裡我們將myHeavyFunction()遷移到一個單獨的執行緒並非同步執行它,但是如果我們這樣做:
mySmallFunction1() launch(UI) { myHeavyFunction() } mySmallFunction2() //Order E/mySmallFunction1 running on: main E/mySmallFunction2 running on: main E/myHeavyFunction running on: main <p class="indent"> |
這裡我們在主執行緒上執行的Coroutine上下文(UI:由coroutine-android庫提供)中執行重量函式,執行仍然是非同步的,因為Coroutines是透過暫停這部分函式處理,但函式執行仍然發生在主執行緒上,而不建立額外的執行緒。
實戰協程
在大多數情況下,我們需要來自一個非同步執行的回撥,這樣我們就可以透過回撥函式來更新UI等,這裡就可以使用Deferred語法:
Deferred本身繼承擴充套件了Job,但增加一個額外的功能,它可以在函式完成執行後返回未來的值。
讓我們看看我們在這裡做了什麼:
fun <T> asyncExecutor(block: () -> T, response: (T) -> Unit): Job { return launch(UI) { val deferred = async(singleThreadDispatcher) { block.invoke() } response.invoke(deferred.await()) } } <p class="indent"> |
讓我們分析一下:
1. launch(UI)使用Android的UI所在的執行緒上下文建立一個協同Job。
2. 我們透過async非同步建立了另一個協同程式,其中包含我們需要呼叫的函式,唯一的區別是:這個協程返回一個Deferred值,async是協程庫的一部分。
3. 我們呼叫await()函式來捕獲Deferred的未來值。這是在UI所線上程上下文中捕獲的。
總而言之,我們建立了一個非同步執行程式,我們可以在其中傳遞函式並讓它們非同步執行,然後將值返回給UI執行緒。
現在我們在哪裡可以使用它 ? 資料庫查詢
// Insert into DB without callback singleThreadAsync { movieDataBase.movieDao().insert(movieObject) } // Get List of movies from DB and filter it asyncExecutor({ movieDataBase.movieDao().getAll() }, { movieList -> movieList .filter { it.isFavorite } .map { it.originalLanguage = "English" } //Dispatch to UI }) <p class="indent"> |
我們將插入到DB的請求變成了一個發射就可以忘記不用等待結果的非同步請求,這是使用singleThreadAsync實現的 。
當我們從DB檢索資料時,我們可以使用我們的asyncExecutor來檢索物件列表,然後使用Collection Framework中的運算子發揮所有kotlin優點啦!
相關文章
- 揭開MSSQL---extents內部的神秘面紗SQL
- 揭開神秘面紗——深入淺出ThreadLocalthread
- 揭開AI、機器學習和深度學習的神秘面紗AI機器學習深度學習
- 揭開正規表示式語法的神秘面紗 (轉)
- 解開“QUIC”的神秘面紗UI
- 揭開神秘面紗,會stream流就會大資料大資料
- NYDIG交易所揭開區塊鏈節點神秘的面紗區塊鏈
- 在Axon框架中揭開跟蹤事件處理器的神秘面紗框架事件
- 揭開SSL的神秘面紗,瞭解如何用SSL保護資料
- 帶你揭開神秘的javascript AST面紗之AST 基礎與功能JavaScriptAST
- 【譯】用 GitHub Copilot 提交註釋揭開歷史的神秘面紗Github
- 揭開ThreadLocal的面紗thread
- 一文揭開JDK21虛擬執行緒的神秘面紗JDK執行緒
- 揭開“QUIC”的神祕面紗UI
- 【C#——揭開你的面紗】C#
- 揭開華為雲CodeArts TestPlan啟發式測試設計神秘面紗!
- 《Kotlin進化之路》之【第二章:揭開Kotlin的基礎面紗】(一)Kotlin
- 《Kotlin進化之路》之【第二章:揭開Kotlin的基礎面紗】(二)Kotlin
- 揭開OKR (Objectives and Key Results) 的面紗OKRObject
- 揭開 Hyperledger Cacti 專案的面紗
- 『MySQL』揭開索引神祕面紗MySql索引
- 揭開Java記憶體管理的面紗Java記憶體
- 揭開計算機的神祕面紗計算機
- 揭開二維碼背後的神秘面紗用二維碼識別 API 就夠了API
- 帶你揭開神秘的Javascript AST面紗之Babel AST 四件套的使用方法JavaScriptASTBabel
- 比MySQL快839倍!揭開分析型資料庫JCHDB的神秘面紗 京東智聯雲開發者MySql資料庫
- 揭開redux,react-redux的神祕面紗ReduxReact
- 揭開Future的神祕面紗——任務取消
- 揭開java記憶體模型的神祕面紗Java記憶體模型
- 揭開Future的神祕面紗——任務執行
- 揭開單體應用程式的神祕面紗
- 揭開DRF序列化技術的神祕面紗
- 揭開JS無埋點技術的神祕面紗JS
- 你未必知道的CSS故事:揭開leading的面紗CSS
- 從一個Demo開始,揭開Netty的神祕面紗Netty
- 重學MySQL-揭開面紗,顯露架構MySql架構
- 揭開網際網路公司的神秘面紗,資料解讀那些slay整個行業的網際網路公司行業
- 揭開NoahV智慧運維前端框架的神祕面紗運維前端框架