🚀 為什麼要使用協程?
📖 什麼是協程?
**協程(Coroutines)**是 Kotlin 的核心功能,讓非同步程式設計變得簡單且直觀。擺脫複雜的回調地獄,就像撰寫同步程式碼一樣簡單!
💡 傳統非同步的問題
回調地獄
// ❌ 回調方式(難以閱讀和維護)
fun fetchUser(userId: String, callback: (User?) -> Unit) {
fetchFromNetwork(userId) { result ->
if (result != null) {
fetchProfile(result.id) { profile ->
if (profile != null) {
fetchPosts(profile.id) { posts ->
callback(User(result, profile, posts))
}
} else {
callback(null)
}
}
} else {
callback(null)
}
}
}
執行緒的成本
// ❌ 執行緒很昂貴
fun downloadFiles() {
repeat(1000) {
Thread {
// 每個執行緒都會消耗大量記憶體
downloadFile("file_$it.txt")
}.start()
}
// 可能會發生記憶體不足!
}
✨ 協程的優勢
1. 簡潔的程式碼
// ✅ 協程(易於閱讀且直觀)
suspend fun fetchUser(userId: String): User? {
val result = fetchFromNetwork(userId) ?: return null
val profile = fetchProfile(result.id) ?: return null
val posts = fetchPosts(profile.id)
return User(result, profile, posts)
}
2. 輕量級並行
// ✅ 即使數千個協程也沒問題
suspend fun downloadFiles() {
coroutineScope {
repeat(10000) {
launch {
// 輕量級協程!
downloadFile("file_$it.txt")
}
}
}
}
3. 結構化並行
// ✅ 自動管理生命週期
suspend fun processData() {
coroutineScope {
val job1 = launch { task1() }
val job2 = launch { task2() }
// 所有任務結束後自動完成
}
}
🎯 實際使用範例
網路請求
// 不需要回調,簡潔俐落!
suspend fun loadUserData(userId: String): UserData {
val user = apiService.getUser(userId)
val posts = apiService.getPosts(userId)
val friends = apiService.getFriends(userId)
return UserData(user, posts, friends)
}
UI 更新
// 不阻塞主執行緒
suspend fun updateUI() {
val data = withContext(Dispatchers.IO) {
// 在背景載入資料
loadDataFromDatabase()
}
// 在主執行緒更新 UI
updateViews(data)
}
並行處理
// 同時執行多個任務!
suspend fun fetchAllData(): CombinedData = coroutineScope {
val users = async { fetchUsers() }
val products = async { fetchProducts() }
val orders = async { fetchOrders() }
CombinedData(
users.await(),
products.await(),
orders.await()
)
}
🔥 協程 vs 執行緒
| 特徵 | 協程 | 執行緒 |
|---|---|---|
| 重量 | 非常輕量 | 笨重 |
| 建立成本 | 幾乎沒有 | 昂貴 |
| 數量 | 可達數萬個 | 數千個極限 |
| 切換成本 | 低 | 高 |
| 取消 | 容易 | 困難 |
效能比較
// 協程:執行 100,000 個
suspend fun testCoroutines() {
val time = measureTimeMillis {
coroutineScope {
repeat(100_000) {
launch {
delay(1000)
}
}
}
}
println("協程:${time}ms") // ~1000ms
}
// 執行緒:就算只有 10,000 個...
fun testThreads() {
val time = measureTimeMillis {
repeat(10_000) {
Thread {
Thread.sleep(1000)
}.start()
}
}
println("執行緒:${time}ms") // 可能記憶體不足!
}
🎨 協程的核心概念
Suspend(暫停)
// suspend 函式只能在協程中呼叫
suspend fun doWork() {
delay(1000) // 不阻塞執行緒的等待
println("任務完成")
}
結構化並行
// 以父子關係管理生命週期
fun main() = runBlocking {
launch { // 父層
launch { // 子層 1
delay(1000)
println("子層 1 完成")
}
launch { // 子層 2
delay(2000)
println("子層 2 完成")
}
println("父層等待子層結束")
}
}
🤔 常見問題
Q1. 協程是新的執行緒嗎?
**A:**不是!協程是在執行緒上執行的輕量級任務單位。
// 一個執行緒可以執行多個協程
fun main() = runBlocking {
repeat(3) { i ->
launch {
println("協程 $i:${Thread.currentThread().name}")
}
}
}
// 可以全部在同一個執行緒中執行!