Android Coroutines 協程
在 Android 開發中,主執行緒 (Main Thread) 負責更新 UI。如果你在主執行緒執行耗時操作(例如網路請求、讀寫資料庫),App 就會卡頓甚至出現 ANR (Application Not Responding)。
Coroutines (協程) 是 Kotlin 提供的非同步處理方案,它讓我們能用同步的方式寫非同步程式碼,既簡單又輕量。
核心觀念:Suspend Function
suspend 關鍵字標記的函式稱為「掛起函式」。
- 它只能在 Coroutine 或另一個 suspend function 中呼叫。
- 當執行到掛起點時,它會暫停目前的執行,讓出執行緒去做別的事,等到結果回來後再恢復執行。
// 定義一個耗時操作
suspend fun fetchUserData(): User {
delay(1000) // 模擬網路延遲,不會卡住執行緒
return User("Alice")
}
啟動 Coroutine:CoroutineScope 與 Builder
要呼叫 suspend function,我們需要建立一個 Coroutine。
launch
launch 是一個 "Fire-and-forget" 的啟動器。它會啟動一個新的 Coroutine,但不回傳結果(回傳 Job 用於控制)。
// 在 ViewModel 中
fun loadData() {
viewModelScope.launch {
// 這裡已經是在 Coroutine 作用域內
val user = fetchUserData() // 執行耗時操作 (掛起)
// 當上面執行完畢,自動回到這裡繼續執行
updateUI(user)
}
}
async
async 用於並發執行並等待結果。它回傳 Deferred<T> (類似 Java 的 Future 或 JS 的 Promise)。你需要呼叫 .await() 來取得結果。
viewModelScope.launch {
// 同時發出兩個請求
val deferredResult1 = async { fetchFromApi1() }
val deferredResult2 = async { fetchFromApi2() }
// 等待兩個都完成
val result1 = deferredResult1.await()
val result2 = deferredResult2.await()
process(result1, result2)
}
切換執行緒:withContext (Dispatcher)
雖然 Coroutine 很聰明,但有時我們需要明確指定在哪個執行緒執行(例如大量的運算)。這時使用 Dispatchers。
- Dispatchers.Main:主執行緒 (UI 操作)。
- Dispatchers.IO:I/O 操作 (網路、資料庫、檔案讀寫)。
- Dispatchers.Default:CPU 密集型運算 (排序、JSON 解析)。
使用 withContext 來切換 Dispatcher:
suspend fun saveImage(bitmap: Bitmap) {
// 切換到 IO 執行緒存檔
withContext(Dispatchers.IO) {
fileOutputStream.write(bitmap)
}
// 自動切回原本的執行緒 (例如 Main)
}
Structured Concurrency (結構化並發)
Kotlin Coroutine 強調結構化並發。這意味著新的 Coroutine 必須在一個特定的 Scope (作用域) 中啟動。
- 當 Scope 被取消時,裡面所有的 Coroutines 也會自動被取消,避免 Memory Leak。
- 在 Android 中,最常用的是:
- viewModelScope:綁定 ViewModel 生命週期(推薦)。
- lifecycleScope:綁定 Activity/Fragment 生命週期。
- rememberCoroutineScope:在 Compose UI 中使用。
// Compose 中的範例
@Composable
fun MyScreen() {
val scope = rememberCoroutineScope()
Button(onClick = {
// 在 click listener 中啟動 coroutine
scope.launch {
// 執行非同步任務
}
}) {
Text("Click Me")
}
}
小結
Coroutine 讓非同步程式碼變得像直線一樣簡單。記住三個關鍵字:
suspend:掛起函式。launch/async:啟動協程。withContext:切換執行緒。
在下一章我們會繼續深入了解 Coroutine 的組態設定 CoroutineContext。