Android Coroutine Context 與 Dispatchers
在上一章我們學會了如何啟動 Coroutine,這一章我們要深入了解 Coroutine 的組態設定 CoroutineContext。
什麼是 CoroutineContext?
每個 Coroutine 都帶有一個 CoroutineContext,它是一組元素的集合,類似於 Map。其中最重要的元素包括:
- Job:控制 Coroutine 的生命週期。
- CoroutineDispatcher:決定 Coroutine 在哪個執行緒執行。
- CoroutineName:給 Coroutine 取名字(方便除錯)。
- CoroutineExceptionHandler:處理未被捕獲的例外。
我們可以使用 + 運算子來組合這些元素:
launch(Dispatchers.IO + CoroutineName("DownloadTask")) {
// ...
}
Job:生命週期控制
Job 是 Coroutine 的把手。當你呼叫 launch 時,它會回傳一個 Job 物件。
val job = scope.launch {
// 執行耗時任務
}
// 在需要的時候取消它
job.cancel()
- job.cancel():取消 Coroutine。
- job.join():等待 Coroutine 執行結束。
父子關係
Coroutine 具有層級關係。
- 如果你在一個 Coroutine (父) 裡面啟動另一個 Coroutine (子),子 Coroutine 會繼承父層的 Context,並且父 Job 會成為子 Job 的 parent。
- 取消父 Job 會遞迴取消所有子 Job。
- 父 Job 會等待所有子 Job 完成後才算完成。
Dispatchers deeply (調度器詳解)
我們在上一章簡介了 Dispatchers,這裡再詳細說明:
Dispatchers.Main
- Android 專用。
- 執行在 UI 主執行緒。
- 用途:呼叫 UI 函式、更新 LiveData/State。
- 底層:使用
Handler(Looper.getMainLooper())。
Dispatchers.IO
- 執行在共享的執行緒池 (Thread Pool)。
- 此池會根據需求彈性擴充 (預設上限為 64 個執行緒或 CPU 核心數)。
- 用途:阻塞性 I/O 操作 (檔案、Socket、Database)。
Dispatchers.Default
- 執行在共享的執行緒池。
- 池大小固定為 CPU 核心數。
- 用途:CPU 密集型運算 (大量數據處理、圖像運算)。
Dispatchers.Unconfined
- 這是一個特殊的調度器。它不限制執行緒,會直接在「當前的執行緒」立即執行,直到遇到第一個掛起點 (suspend point)。恢復執行時,則會在恢復的執行緒上繼續。
- 實務上極少使用,除非你有特殊需求。
Exception Handling (例外處理)
在 Coroutine 中發生 Crash 會怎樣?
- 如果使用
launch,例外會被視為「未捕獲例外」,預設會導致 App 崩潰。 - 如果使用
async,例外會被封裝在Deferred中,直到你呼叫.await()時才拋出。
CoroutineExceptionHandler
我們可以使用 CoroutineExceptionHandler 來全域捕獲 launch 中的例外:
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception")
}
scope.launch(handler) {
throw RuntimeException("Oops!") // 不會導致 App 崩潰
}
注意:
CoroutineExceptionHandler 只對 root coroutine (最外層) 有效。如果是子 Coroutine 拋出例外,它會委派給父 Job 處理,直到傳遞到 root。SupervisorJob
預設情況下,如果一個子 Coroutine 失敗,它會導致父 Job 取消,進而導致所有兄弟 Coroutine 也被取消。如果這不是你想要的行為(例如:同時下載 3 張圖片,一張失敗不應影響其他兩張),可以使用 SupervisorJob 或 supervisorScope。
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
// 這個 scope 下的子 coroutine 失敗時,不會影響其他兄弟
小結
CoroutineContext 就像是 Coroutine 的設定檔。了解 Job 的層級關係與 Dispatcher 的調度機制,能讓你更精準地控制非同步任務的行為與穩定性。