# Android筆記--kotlin Coroutines(kotlin 協程) Kotlin 協程(Coroutines)是輕量級的非阻塞異步機制,讓你以直觀、可讀的方式撰寫異步程式。 <b><u>Coroutine的特點是</u></b>: - 可暫停(suspend)並保留執行狀態,之後恢復(resume)接續執行 - 可透過 Dispatcher 在執行緒間切換而不用自己管理鎖或同步等線程安全問題,適合處理 I/O 與併發工作。 - 工作原理是將工作拆分成更小的單位,稱為協程,每個協程都可以在其自己的執行緒上運行,但協程之間可以共享狀態 <details><summary>Coroutine與Thread</summary> <div> >[!Note]參考: >- [Android筆記(kotlin)--Thread、Handler Class、Looper、Message Queue](https://hackmd.io/UfXHFmo6TJqPohvl5qAWVA) >- [Android筆記--AsyncTask(已廢棄,插件)](https://hackmd.io/Ak9X6Q-mTBaMTO-chU5zxQ) Kotlin coroutines是Thread或是AsyncTask的替代方案。 <b><u>Coroutine和Thread的差別在於</u></b>: - Thread:程式碼直接交由作業系統排程器執行,屬於 搶佔式 (Preemptive) 多工,系統可隨時切換執行緒。 - Coroutine:程式碼先由 Coroutine 框架管理,再分派到 Thread 上執行,屬於 協同式 (Cooperative) 多工,只有在程式碼「主動掛起 (suspend)」時才會讓出執行權。 :::success :::spoiler ※===Coroutine和Thread執行方式的比較===※ - 使用Thread的方式: 直接暴力的把Code塞進Thread後再塞給底層系統,由於採用"搶佔式"的運作方法容易造成系統堵塞(想像漏斗被大顆的東西卡住)  - 使用Coroutine的方式:Code會先交由Coroutine框架,由框架進行去分配順序,再來框架會將 Coroutine 物件調度到一個可用的 Thread,Thread再將Code交給底層執行。  ::: </div> </details> <details><summary>Coroutine與RxJava</summary> <div> RxJava (ReactiveX)的核心是「資料流 (Stream) + 事件推送 (push-based)」,一切都看作資料流:網路回應、使用者點擊、資料庫回傳……都能用 Observable/Flowable 來處理。 - 適合需要「複雜資料流組合與轉換」的場景,例如:合併多個 API 結果、處理連續點擊防抖。 - 在新專案裡,Coroutine 幾乎能取代 80% RxJava 的使用場景,只有在超複雜的資料流場景才會看到 RxJava 還有優勢。 <b><u>適合場景</u></b>: - RxJava:功能超強大,但學習難度高,像「瑞士刀」,什麼都有。 - 需要處理「多來源資料流」 - 事件流 (event stream) 很多,比如手勢處理、連續點擊、股票報價更新 - 專案原本就是 RxJava 架構(換掉成本太高) - Coroutine:寫法簡單直覺,像「日常刀叉」,適合大部分需求。 - 主要需求是「簡單的非同步處理」 - 網路請求 + 資料庫存取 + UI 更新 - 想要可讀性高、Kotlin 友好 </div> </details> <details><summary>Coroutine、RxJava、Thread三者比較</summary> <div> | 特性 | **Thread** | **RxJava** | **Coroutine** | | -------- | -------------------- | -------------------------------- | ---------------------------------- | | **抽象層級** | 最低,直接控制執行緒 | 中等,基於 Thread 的資料流框架 | 最高,語法層級的非同步抽象 | | **本質** | 作業系統分配的執行單位 | Reactive Streams(事件驅動) | 協程(協同式多工,輕量級) | | **多工模式** | **搶佔式** (Preemptive) | 基於搶佔式 Thread pool | **協同式** (Cooperative),需要 `suspend` | | **效能** | 重,建立/切換成本高 | 較佳,靠 thread pool 管理 | 最佳,輕量,單 Thread 可跑數萬協程 | | **程式風格** | 傳統 callback,手動切換執行緒 | 鏈式操作 (map, flatMap, zip) | 類同步風格,用 `suspend` 直覺撰寫 | | **錯誤處理** | 例外要手動捕捉 | `onError()` callback 或 operators | 直接用 `try/catch` | | **學習曲線** | 低,但容易踩坑(同步、鎖、資源競爭) | 高,需要理解 operators、背壓 | 中,需要理解 `suspend`、作用域 | | **適合場景** | 簡單非同步、一次性工作 | 複雜資料流、事件流處理(例如合併多 API、點擊防抖) | 大部分非同步需求(網路、資料庫、UI 更新) | </div> </details> --- ## ▼啟用: 到AndroidDeveloper尋找Coroutine的依附元件地址,並新增到`build.gradle.kts` - [AndroidDeveloper--Android 上的 Kotlin 協同程式](https://developer.android.com/kotlin/coroutines?hl=zh-tw) ``` dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9") } ``` 備註:如果Coroutine要搭配[Room](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/Android筆記_ROOM)使用的話,要再加上Room依賴項 - [ROOM官網](https://developer.android.com/jetpack/androidx/releases/room?hl=zh-tw) ``` val room_version = "2.6.1" implementation("androidx.room:room-ktx:$room_version") ``` --- ## ▼超基本使用方法: 1.首先,先將打算放進Coroutine中的fun()標記為[suspend](#▼suspend介紹)(此處以Room的操作為例,[Android筆記--Room](https://hackmd.io/kD5kTSH1T-a9yKoJlXRLxA#%E2%96%BC%E2%80%BB%E4%BD%BF%E7%94%A8Singleton-%E8%A8%AD%E8%A8%88%E6%A8%A1%E5%BC%8F%E6%94%B9%E5%AF%AB%E2%80%BB))  2.製作一個[**CoroutineContext**](#▼CoroutineContext介紹)物件,在其中用"+"號來將想要的設定合成一個CoroutineContext物件。(其實也可以不製作CoroutineContext,直接把設定參數丟進CoroutineScope中) 備註:如果不在CoroutineContext中加入Job()的話,Coroutine就會自動管理生命週期, 3.呼叫[**CoroutineScope()**](#▼Scope介紹),並將剛剛製作的CoroutineContext物件作為參數傳入,再呼叫[**launch()** 或 **async()**](#▼launch和async介紹) 來啟動Coroutine。  ``` //Coroutine的基本構造 val context = Dispatchers.IO // 將IO 調度器組合成協程上下文 // 使用 CoroutineScope 創建一個協程 val coroutineScope = CoroutineScope(context) coroutineScope.launch { // 在 IO 線程中執行的協程內容 } ``` ==但是,使用這種寫法在不同的情況下可能會造成內存洩漏(雖然Coroutine會自動管理生命週期,但在某些情況下Job可能會沒有隨著線程的結束而一起結束)以及維護的問題,在這裡提供這種寫法只是為了方便理解Coroutine在文法上的基本架構。== 參考: [※推薦的使用方法](#※推薦的使用方法1:) --- ### ▼suspend介紹 在 Kotlin 中,suspend 關鍵字用於標記協程函式。協程函式是一種特殊的函式,==可以暫停其執行而不會阻塞線程,並且可以在需要時恢復執行。這使得您可以以非阻塞方式編寫程式碼,而無需擔心鎖或其他同步機制==,通常用於進行耗時的操作,如網絡請求、IO 操作或其他需要等待的異步任務。 - 當一個函數被聲明為 suspend 時,它才可以在 Coroutine 中被暫停 - 如果在coroutine中調用了非suspend的fun(),這個fun()會立刻的被執行,而不是如我們所期望的"以Coroutine的方式"執行(coroutine核心運作機制是建立在暫停與恢復之上),也就是說,如果該非 suspend 函式執行耗時操作,則可能會阻塞 Coroutine 並導致主線程卡頓(因為該fun()不會被暫停且會立刻執行)。 - 如果該非 suspend 函式拋出異常,則該異常將無法被CoroutineExceptionHandler(Coroutine的異常處理器,被包含在CoroutineContext中)正確處理。 suspend 關鍵字具有以下注意事項: - suspend 函式只能在其他 suspend 函式或標記為 fun 的函式中調用。 - suspend 函式不能直接調用非 suspend 函式。 - suspend 函式不能直接調用靜態函式。 --- ### ▼CoroutineContext介紹 CoroutineContext 是一種用於定義 Coroutine 執行環境的工具(描述協程上下文的介面),包含了各種協程屬性的集合。 ==簡白的來說,CoroutineContext是一組要填入CoroutineScope()中的參數集合體,描述了協程的執行環境,包括了調度器、生命週期、異常處理器等信息==。 :::warning :::spoiler 以下是一些常用的 CoroutineContext 元素: - **Dispatcher**: 指定了協程的執行緒或者線程,例如 Dispatchers.Main 表示在主 UI 線程中運行,Dispatchers.IO 表示在 IO 線程池中運行,Dispatchers.Default 表示在 CPU 密集型的默認線程池中運行。 :::success - :::spoiler ※===Dispatcher詳細介紹===※ - Dispatcher 的意思是調度器,那其實就是完整包裝好提供開發者輕鬆使用的 CoroutineContext Element,負責控制thread之間的切換 - 簡白地說,==Dispatcher 是用於指定協程運行在哪個執行緒上的機制==。它決定了協程在執行時使用的執行緒池或線程,從而影響了協程的調度和執行方式。 - Dispatcher用於指定協程運行的執行緒或執行緒池(控制著協程在哪個線程上運行以及如何運行)。 - 通過將 Coroutine 分配到不同的 Dispatcher 上,您可以確保 CPU 密集型任務不會阻塞 UI 線程,從而防止 UI 凍結 - Dispatcher 可以通過 withContext() 函數在協程內部進行切換,從而根據需要調整協程的執行緒。 :::info - 常見的 Dispatcher: - **==Dispatchers.Default==**: 就是執行預設的 CoroutineScheduler(coroutine調度程式),是用於 CPU 密集型操作的 Dispatcher。它使用背景的共享執行緒池來執行。在這個池中,執行緒的數量會等於 CPU 內核的數量,不過最少會是 2,適用於需要==執行大量運用cpu資源運算的工作==,原則上是會開另一個 Thread,通常不會跑在 Main thread 上。 - ==**Dispatchers.IO**==: 這是用於IO操作的Dispatcher。它使用一個或多個後台執行緒來執行協程,適用於==執行磁盤讀寫、網絡請求==等 IO 相關的任務。 - 它與 Dispatchers.Default 的區別在於,Dispatchers.IO 使用的是 I/O 運行緒池,適用於需要進行長時間等待的 IO 操作。 - 是基於 Default 去加強的 Dispatchers ,它跟 Default 最本質的區別在於 ,它開的 thread 數量會比較多,通常它與 Default 都是負責不把 Main thread 堵住的耗時處理。 - **==Dispatchers.Main==**: 就是 Main thread 的包裝,在 Android 需要操作到 UI thread 通常會用它,是==用於 UI 相關操作==的 Dispatcher。它將協程的執行流程切換到主 UI 線程,用於更新 UI 元素或處理與 UI 相關的任務。在 Android 中,Dispatchers.Main 通常與 Android 主線程關聯,因此它用於在 UI 上運行協程。 - 用於和界面交互和快速執行的工作 - 在做完耗時的任務之後,最後如果需要更新畫面,我們就必須要把執行緒切回主執行緒上。 - 我們可以用[withContext()](#▼一些其他的功能、方法)切換從其他的執行序切換到Disapatchers.Main - **==Dispatchers.Unconfined==**: 可將協程在調用的執行緒上立即運行,然後恢復執行緒的上下文(也就是說,Unconfined 調度器在協程開始運行時不會將其限制在特定的執行緒上,而是會在當前的執行緒上立即運行協程,直到遇到挂起點(如 delay() 或 suspend 函數))。這個 Dispatcher 不應用於 IO 或長時間運行的操作,而是適用於某些特殊情況下的需求。 - 它並不適合用於 IO 操作或需要長時間運行的任務,因為它缺乏在背景執行緒上執行的保證。 - **Job**: 表示協程的生命週期,控制協程的啟動、取消等操作。 :::success - :::spoiler ※===Job詳細介紹===※ - Job 就是 Coroutines的控制代碼(控制器),更確切來說,使用 launch 或 async 建立的每個協同程式都會傳回 Job 執行個體,用來以唯一的方式識別協同程式,並管理生命週期。 - 每當啟動一個協程時(創建一個Coroutine時),都會返回一個對應的 Job 實例。例如,使用 launch 函數啟動協程時,會返回一個代表該協程的 Job。 - 也可以自己實例一個Job,作為Coroutine context的參數傳入,並透過這個Job管理coroutine - 我們可以簡單的使用job.cancel()來取消和job關聯的Coroutine :::info - :::spoiler ※===檢查Job是否正在運作===※ 當我們需要檢查Job是否正在運行時,可以使用`Job`中的`isActive`屬性或是`ensureActive()`方法 - **isActive**:這是 Job 的一個屬性,用於檢查協程是否處於活動狀態,即協程是否仍在運行並且尚未被取消。當協程處於活動狀態時,isActive 返回 true,否則返回 false。 - ``` for (i in (1..10)){ if (this.isActive){ Thread.sleep(100) Timber.d(i.toString()) } } //或是 while(i <= 10 && this.isActive) - **ensureActive()**:這是一個 Job 的擴展函數,用於確保協程處於活動狀態。如果協程已經被取消,則 ensureActive() 會拋出 CancellationException,以防止在已取消的協程中繼續執行代碼。 - ``` for (i in (1..10)){ ensureActive() Thread.sleep(100) Timber.d(i.toString()) } ``` :::info - :::spoiler ※===supervisorJob介紹===※ - Supervisor Job 是一種特殊的 Job,用於管理其下的子協程。與普通的 Job 不同,SupervisorJob 在其中的子協程遇到異常時,不會影響到其他子協程的執行。 - 換句話說,當一個子協程由於某些原因而失敗時,Supervisor Job 不會將該異常傳播給其他子協程,而是繼續執行其他子協程。 - 想達成一樣的效果但是不想使用Job()的話,也可以使用SupervisorScope - ``` val scope = CoroutineScope(SupervisorJob()) scope.launch { //do something } ``` - **CoroutineExceptionHandler**: 異常處理器,用於處理協程執行過程中出現的異常。 - **CoroutineName**: 協程的名稱,用於標識和區分不同的協程。 ::: --- ### ▼Scope介紹 Scope(作用域)是指定協程的上下文和生命週期的概念。它==通常表示一個協程的作用域或範圍==,決定了協程的啟動、運行和取消等行為。簡白的說,==Scope 是管理 Coroutine 生命週期的容器,可以在scope中創建和管理coroutine==。 - scope通常表示一個協程的作用域或範圍,決定了協程的啟動、運行和取消等行為。 - Scope 可以幫助您確保 Coroutine 在不再需要時被取消,從而釋放資源並防止內存洩漏。 - Scope 確保協程的運行和取消等行為符合應用程序的需求和預期。 :::warning - :::spoiler ※===Scope的常見類型===※ - **GlobalScope**: 全局作用域,通常用於創建全局性的協程,其生命週期與應用程序的生命週期相關聯。全局作用域的協程不受限於任何特定的生命週期,但可能導致協程泄漏或生命周期管理上的困難。 - **CoroutineScope**: 協程作用域,通常用於創建局部範圍的協程,其生命週期與指定的作用域相關聯。當作用域結束時,與該作用域相關的協程也將被取消。CoroutineScope 可以是 CoroutineScope() 函數的返回值,也可以是實現了 CoroutineScope 接口的類的實例。 - **ViewModelScope**: ViewModel 作用域,是 Android Jetpack 中 ViewModel 相關組件所提供的一種協程作用域,用於在 ViewModel 中啟動協程。它的生命週期與 ViewModel 的生命週期相關聯,當 ViewModel 被銷毀時,與之相關的協程也將被取消。 ::: :::info ==特殊的Scope:== --- - :::spoiler ※===LifecycleScope介紹===※ - **LifecycleScope**: 生命周期作用域,是 Android Jetpack 中 Lifecycle 相關組件所提供的一種協程作用域,用於在 Android 中與生命周期進行協作。它的生命週期與相關聯的生命周期相關聯,例如 Activity 或 Fragment 的生命週期。當相關聯的生命週期結束時,與之相關的協程也將被取消。 - 如果要使用LifecycleScope,需要額外載入["lifecycle-viewmodel-ktx庫"](https://developer.android.com/kotlin/ktx?hl=zh-cn#viewmodel),因為LifecycleScope屬於`lifecycle-viewmodel`的一部分 - 參考: ["androidx.lifecycle"](https://developer.android.com/jetpack/androidx/releases/lifecycle?hl=zh-tw)、[Android筆記(kotlin)–ViewModel](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_ViewModel) - ``` dependencies { implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2") } ``` - :::spoiler ※===supervisorScope介紹===※ - ==使用 supervisorScope 可以創建一個獨立的協程範圍,其中的子協程可以獨立失敗而不會影響到其他子協程的執行==。 - 這在需要容忍某些子協程失敗的情況下非常有用,同時不希望整個協程被取消。 - 想達成依樣的效果但是不想使用Scope的方式的話,也可以使用supervisorJob。 - ``` val scope = CoroutineScope(Job()) scope.launch { supervisorScope { launch { // Child 1 } launch { // Child 2 } } } ``` ::: --- ### ▼launch和async介紹 launch和async都是用於啟動 Coroutine 的函式(==創建新的Coroutine並在指定的Thread中運行==),它們之間的主要區別在於 async 會返回一個 Deferred 物件(deferred< value>),而 launch 則不會。 :::warning :::spoiler ※===launch()和async()的比較===※ - **launch**: :::success - launch 用於啟動一個新的協程,它並不返回任何結果。 - 語法:launch { ... } - 常用於執行一些非同步任務,例如從網絡請求數據、IO 操作等,不需要返回結果的場景。 - launch 函數啟動的協程不會返回任何值,因此不能使用 await 函數獲取結果。 - 任何視為「射後不理」的工作都可以使用 launch 啟動。 - **async**: :::success - 當兩個任務之間沒有相互關係時,async併發可以讓我們減少等待執行的時間 - async 也用於啟動一個新的協程,但它返回一個 Deferred 對象(deferred< value>),該對象包裝了協程的計算結果。 - 語法:val deferred = async { ... } - async 通常用於需要返回結果的異步任務,例如計算、耗時操作等。 - 通過 await 函數,可以獲取 async 啟動的協程計算的結果。 await 函數會等待協程執行完成並返回結果。 - 因為無法確定非同步任務會在何時結束,不論是運算結果或是Exception,他都預期開發者會呼叫await()掛起函數,以獲得回傳值或是錯誤訊息,所以預設並不會丟出exception(白話:要呼叫await()才會拿到結果) - launch 用於執行不需要返回結果的協程,而 async 用於執行需要返回結果的協程。根據任務的需求和特點,選擇適合的協程建構器。 - async預期開發者會呼叫await來獲得結果(或是exception),換句話說,他會默默地丟出exception,而只有在呼叫await你才會知道拿到result或是exeption,而launch會直接丟出來 ::: --- ### ▼一些其他的功能、方法 1.**withContext()**: 在當下的 Coroutine scope 中,使用不同的 CoroutineContext :::success - :::spoiler ※===withContext()===※ - 一個用於切換執行上下文的函數。它允許在協程內部切換到不同的執行緒或調度器中執行代碼,然後返回到原始的執行上下文。 - 具體來說,withContext() ==接受一個 CoroutineContext 參數,該參數指定了要在其中執行代碼的執行緒或調度器==。然後,它會將執行上下文切換為指定的上下文,執行傳入的代碼塊,並在代碼執行完成後恢復原始的執行上下文。 ::: 2.**delay()**: 暫停執行協程一段時間,==以毫秒為單位==。在指定的時間內,協程會暫停執行,然後再繼續執行後面的代碼。 3.**yield()**: 將執行權交還給協程調度器,讓其他協程有機會執行。它在協程中的某些點可以顯式調用,以便進行上下文切換。也就是把目前 Coroutine 調度器執行的任務暫停並讓給其他的任務來執行。 4.**withTimeout()**: 設置一個執行超時時間,在指定的時間內,如果協程未完成,則會導致 TimeoutCancellationException 錯誤。 5.**withTimeoutOrNull()**: 這個函數與 withTimeout() 類似,但是在超時時不會引發異常,而是返回 null。 6.**awaitAll()**: 等待多個 Deferred 的完成(或是直到其中一個發生錯誤)。當你有多個 Deferred 對象時,你可以使用 awaitAll() 來等待它們全部完成。 7.**join()**: 等待一個Job的完成。當你啟動一個新的Job後,通常你希望等待它執行完畢,然後再繼續後面的代碼。 8.**joinAll()**: 等待多個 Job 的完成。當你有多個 Job 對象時,你可以使用 joinAll() 來等待它們全部完成。 9.**channel**: 是一種用於在協程之間傳遞值和訊息的溝通機制,==假設我們希望能夠在執行到一半的時候就把目前的值傳出來的時候就要使用channel==。 :::success - :::spoiler ※===channel介紹===※ - Channel 的機制類似 Queue,資料是採取FIFO(First in first out)的方式傳出 - 使用send()將資料放進Channel,使用receive()將資料從channel中取出。  參考: [Day15:Channel 的第一堂課](https://ithelp.ithome.com.tw/articles/10268686) ::: --- ## ※推薦的使用方法1: 此種寫法改進於[※超基本使用方法](#※超基本使用方法:),  ## ※推薦的使用方法2: 此種寫法相較於[※超基本使用方法](#※超基本使用方法:),明確的指定了Job()的運作方式,因此擁有了更好的操作性,缺點是需要手動結束Job()。 1.加上CoroutineScope介面,並實作CoroutineContext 2.製作此Activity專屬的Coroutine專屬的Job(控制器)並放入被Override的CoroutineContext中 3.最後使用我們製作的Job物件手動關閉此Coroutine ==備註:由於我們使用了CoroutineScope介面,因此原本的`CoroutineScope().launch{}`要改成`launch{}`== 備註2:當然launch中還是只能放有加上suspend關鍵字的方法。 --- ## 重要概念: - kotlin 的coroutine只是一個線程框架,他是將線程包裝成方便的版本,launch不是 thread,Dispatcher也不是thread,他們是將任務透過handler post到 thread 的looper裡面。 - coroutineScope並不是建立一個Coroutine,而是建立一個Scope --- ## GitHub: https://github.com/PudCheetah/MyPratice_Coroutine_1.git --- ## 參考資料: - [Kotlin Playground 中的協同程式簡介](https://developer.android.com/codelabs/basic-android-kotlin-compose-coroutines-kotlin-playground?hl=zh-tw#0) - ==★==[Kotlin Coroutines 那一兩件事情](https://medium.com/jastzeonic/kotlin-coroutine-%E9%82%A3%E4%B8%80%E5%85%A9%E4%BB%B6%E4%BA%8B%E6%83%85-685e02761ae0) - ==★==[Kotlin Coroutines: 入門概念 Coroutine vs Thread](https://medium.com/gogolook-tech/kotlin-coroutines-%E5%85%A5%E9%96%80%E6%A6%82%E5%BF%B5-coroutine-vs-thread-e7d112b0d8ba) - [Android Developers--Android 上的 Kotlin 協同程式](https://developer.android.google.cn/kotlin/coroutines?hl=zh-tw) - [Android Developers--協同程式簡介](https://developer.android.com/codelabs/basic-android-kotlin-training-introduction-coroutines?hl=zh-tw#0) - ==★==[Android Developers--使用 Kotlin 協同程式提升應用程式效能](https://developer.android.com/kotlin/coroutines/coroutines-adv?hl=zh-tw) - [Android Thread & Relation With Coroutine](https://medium.com/@raya.wahyu.anggara/android-thread-43b30b61dee6) - [Scope和Context](https://cmmobile.gitbook.io/androidbook/duo-zhi-hang-xu/kotlin-coroutine/scope-he-context) - [筆記: Coroutine簡介(一) 簡單介紹與線程切換](https://medium.com/@alice.margatroid.love/%E7%AD%86%E8%A8%98-coroutine%E7%B0%A1%E4%BB%8B-%E4%B8%80-%E7%B0%A1%E5%96%AE%E4%BB%8B%E7%B4%B9%E8%88%87%E7%B7%9A%E7%A8%8B%E5%88%87%E6%8F%9B-4d6786c9b0b7) - [筆記: Coroutine簡介(二) Async與非結構化/結構化並發](https://medium.com/@alice.margatroid.love/%E7%AD%86%E8%A8%98-coroutine%E7%B0%A1%E4%BB%8B-%E4%BA%8C-async%E8%88%87%E9%9D%9E%E7%B5%90%E6%A7%8B%E5%8C%96-%E7%B5%90%E6%A7%8B%E5%8C%96%E4%B8%A6%E7%99%BC-3f6c16ec1177) - [筆記: Coroutine簡介(三) viewModelScope、liveCycleScope與livedata builder](https://medium.com/@alice.margatroid.love/%E7%AD%86%E8%A8%98-coroutine%E7%B0%A1%E4%BB%8B-%E4%B8%89-viewmodelscope-livecyclescope%E8%88%87livedata-builder-e8be67aade8) - [筆記: Coroutine簡介(四) 使用Flows](https://medium.com/@alice.margatroid.love/%E7%AD%86%E8%A8%98-coroutine%E7%B0%A1%E4%BB%8B-%E5%9B%9B-%E4%BD%BF%E7%94%A8flows-724da079e531) - [筆記: Coroutine簡介(五) 中繼運算子、狀態流與分享流](https://medium.com/@alice.margatroid.love/%E7%AD%86%E8%A8%98-coroutine%E7%B0%A1%E4%BB%8B-%E4%BA%94-%E4%B8%AD%E7%B9%BC%E9%81%8B%E7%AE%97%E5%AD%90-%E7%8B%80%E6%85%8B%E6%B5%81%E8%88%87%E5%88%86%E4%BA%AB%E6%B5%81-860ce22d8b65) - ==★==[iThone鐵人賽--Coroutine 停看聽 系列](https://ithelp.ithome.com.tw/2020-12th-ironman/articles/3966) - :::spoiler iThone鐵人賽--你說用coroutine講30天會不會太過分 - [解鎖kotlin coroutine的各種姿勢-新手篇(總覽)](https://ithelp.ithome.com.tw/users/20141766/ironman/4757) - [day1 你說用coroutine講30天會不會太過分](https://ithelp.ithome.com.tw/articles/10265118) - [day 2 coroutine和架構組件](https://ithelp.ithome.com.tw/articles/10265971) - [day3 讓我看看,什麼是Coroutine Scope](https://ithelp.ithome.com.tw/articles/10266383) - [day 4 I'm your father, coroutine父子繼承關係](https://ithelp.ithome.com.tw/articles/10267488) - [day 5 knock, knock我要開始coroutine](https://ithelp.ithome.com.tw/articles/10268345) - [day6 阿伯出事啦 exception](https://ithelp.ithome.com.tw/articles/10269005) - [day7 我不要了,這不是肯德基 cancel](https://ithelp.ithome.com.tw/articles/10269651) - [day8 kotlin coroutine的 runBlocking, withContext](https://ithelp.ithome.com.tw/articles/10270487) - [day9 Kotlin coroutine 的黑魔法 suspend](https://ithelp.ithome.com.tw/articles/10271228) - [day10聊聊structure concurrency 結構化併發](https://ithelp.ithome.com.tw/articles/10271874) - [day11 Kotlin coroutine 花生什麼事?](https://ithelp.ithome.com.tw/articles/10272525) - [day12 輕鬆一下,用 coroutine 接個 restful api](https://ithelp.ithome.com.tw/articles/10273143) - [day13 Kotlin coroutine channel操作](https://ithelp.ithome.com.tw/articles/10273902) - [day14 channel實戰使用 with webSocket,後面離題講android接localhost](https://ithelp.ithome.com.tw/articles/10274560) - [day15 job的騷操作](https://ithelp.ithome.com.tw/articles/10275209) - [day16 coroutine job 的那些狀態,job state](https://ithelp.ithome.com.tw/articles/10275772) - [day17 不懂kotlin flow資料流? 那喝杯進口奶茶吧](https://ithelp.ithome.com.tw/articles/10276330) - [day18 kotlin - flow基本操作](https://ithelp.ithome.com.tw/articles/10276784) - [day19 Kotlin coroutine flow with liveData in MVVM](https://ithelp.ithome.com.tw/articles/10277318) - [day20 在ui蒐集flow,能取代liveData嗎?](https://ithelp.ithome.com.tw/articles/10277912) - [day21 開分支,淺談kotlin paging3 with flow](https://ithelp.ithome.com.tw/articles/10278328) - [day22 熱流sharedFlow](https://ithelp.ithome.com.tw/articles/10278861) - [day23 stateFlow狀態流,又是沒梗的一天](https://ithelp.ithome.com.tw/articles/10279181) - [day24 stateflow和shareflow是如何取代livedata的,聊聊use case吧!!](https://ithelp.ithome.com.tw/articles/10279626) - [day25 矮額是callback,把它變成flow好了 簡單的callbackFlow](https://ithelp.ithome.com.tw/articles/10280046) - [day26 老闆我趕時間,給我最快完成的料理 select](https://ithelp.ithome.com.tw/articles/10280446) - [day27 coroutine和任務的愛情長跑,application和workManager](https://ithelp.ithome.com.tw/articles/10280799) - [day28 等一下啦,會壞掉的/// Coroutine併發操作的重複請求](https://ithelp.ithome.com.tw/articles/10281136) - [day29 大量操作怎麼辦? 連volatile都救不了我QQ](https://ithelp.ithome.com.tw/articles/10281496) - [day30 Kotlin coroutine 結賽統整](https://ithelp.ithome.com.tw/articles/10281773)
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up