---
# System prepended metadata

title: '[Video Note] [Android] [Kotlin] Philipp Lackner - KOTLIN COROUTINES'
tags: [Video Note, Kotlin]

---

# WHAT IS A COROUTINE?
## Kotlin 的 Coroutine 介紹
- Coroutine 是一種 Kotlin 中的協程概念，受到 Google 的高度支持。
- 協程的特性使其在 Android 開發中非常受歡迎。

## 函數與執行緒的背景知識
- 函數：一系列指令的執行，接受輸入並返回輸出。
![CleanShot 2024-08-24 at 22.50.43](https://hackmd.io/_uploads/rkELmOvo0.png)

- 執行緒：指示在哪個上下文中執行這些指令，通常是線性執行。
![CleanShot 2024-08-24 at 22.50.52](https://hackmd.io/_uploads/B1R8XOPiR.png)
![CleanShot 2024-08-24 at 22.51.03](https://hackmd.io/_uploads/BkKwXuwiA.png)

## 單執行緒的限制
- 在沒有多執行緒的情況下，所有指令會在同一個執行緒中依序執行。
- 這可能導致問題，例如進行網路請求時會凍結 UI，因為主執行緒在等待回應。

## 多執行緒的重要性
- Android 中的指令通常在主執行緒中執行，主執行緒負責更新 UI。
![CleanShot 2024-08-24 at 22.51.38](https://hackmd.io/_uploads/S19FXOwoC.png)

- 如果進行網路請求等耗時操作而不使用多執行緒，會導致 UI 停止更新。
- 因此，我們會啟動一個新的執行緒來處理這些耗時操作，避免阻塞主執行緒。
![CleanShot 2024-08-24 at 22.51.46](https://hackmd.io/_uploads/Bk49mOPoA.png)

## Coroutine 的優勢
![CleanShot 2024-08-24 at 22.52.17](https://hackmd.io/_uploads/ry6sQuPsA.png)

- Coroutine 可以做執行緒能做的事情，並且更高效。
- 協程是輕量級的執行緒，允許在單一執行緒中啟動多個協程。
- 協程可以暫停執行並在需要時繼續，這是執行緒無法做到的。
- 協程可以輕鬆切換上下文，即從一個執行緒切換到另一個執行緒。

## Coroutine 的效能展示
- 啟動 100,000 個協程來打印字元，執行速度非常快且不會出現內存不足錯誤。
- 相同操作用 100,000 個執行緒來執行，會導致內存不足錯誤。
![CleanShot 2024-08-24 at 22.53.32](https://hackmd.io/_uploads/SysxVdDs0.png)
![CleanShot 2024-08-24 at 22.52.52](https://hackmd.io/_uploads/BkHkN_PiR.png)

# Starting our First Coroutine
## 在 Kotlin 中啟動第一個 Coroutine
- 要啟動第一個 Coroutine，首先需要在專案中加入 Coroutine 相關的依賴項目。
- 開啟 `build.gradle` 中的 `dependencies` 區塊，加入相關依賴並同步。
![CleanShot 2024-08-24 at 23.20.13](https://hackmd.io/_uploads/HJY49OvsC.png)

## 在 MainActivity 中啟動 Coroutine
- 使用 `GlobalScope.launch` 可以啟動一個 Coroutine，這是最簡單的方式，但並不是最佳方式。
- `GlobalScope` 表示這個 Coroutine 的生命週期將與整個應用程序一樣長。

## 觀察 Coroutine 的執行緒
![CleanShot 2024-08-24 at 23.21.41](https://hackmd.io/_uploads/B1Lqq_woC.png)

- 可以透過 `Log.d` 來記錄 Coroutine 所在的執行緒名稱。
- 在 `GlobalScope` 中啟動的 Coroutine 將在另一個執行緒中執行，這樣可以確認 Coroutine 是異步執行的。
![CleanShot 2024-08-24 at 23.21.57](https://hackmd.io/_uploads/H1Gi9dPoR.png)

## 使用 Delay 暫停 Coroutine
- Coroutine 可以使用 `delay` 函數暫停指定的時間，類似於執行緒的 `sleep` 函數，但不會阻塞整個執行緒。
- `delay` 只會暫停當前的 Coroutine，不會影響同一執行緒中的其他 Coroutine。
![CleanShot 2024-08-24 at 23.23.13](https://hackmd.io/_uploads/rk21o_wjC.png)

## 主執行緒結束時的行為
- 當主執行緒結束時，所有的執行緒和 Coroutine 也會被取消，即使它們在其他執行緒中運行。
- 這可以通過延長 `delay` 時間並立即退出應用來觀察，這樣 Coroutine 的輸出就不會顯示出來。
![CleanShot 2024-08-24 at 23.24.09](https://hackmd.io/_uploads/SkH7s_PiA.png)

# Suspend Functions
## Coroutine 中的 Suspend 函數介紹
- `delay` 函數是一個 `suspend` 函數，它只能在另一個 `suspend` 函數或 Coroutine 中調用。
Ctrl + Q
![CleanShot 2024-08-24 at 23.27.49](https://hackmd.io/_uploads/ByQZ2dviR.png)
![CleanShot 2024-08-24 at 23.28.59](https://hackmd.io/_uploads/rJPHh_wjR.png)
- 在 `GlobalScope.launch` 中啟動 Coroutine 後，可以調用 `delay` 函數來暫停 Coroutine。


## Suspend 函數的特點
- Suspend 函數只能在 Coroutine 或其他 Suspend 函數內部調用。
- 這與一般函數不同，無法在普通上下文中直接使用 Suspend 函數。

## 自定義 Suspend 函數
- 可以自定義 Suspend 函數，像這樣：
![CleanShot 2024-08-24 at 23.29.33](https://hackmd.io/_uploads/rJFwh_PiA.png)
![CleanShot 2024-08-24 at 23.29.56](https://hackmd.io/_uploads/S1lYndvoR.png)

- 在 Coroutine 內部可以調用這些自定義的 Suspend 函數，模擬一些需要時間的操作，例如網絡請求。

## 多個 Suspend 函數的影響
- 當在同一 Coroutine 中依次調用多個 Suspend 函數時，它們的執行時間會累加。
- 例如，兩個各延遲 3 秒的 Suspend 函數在同一 Coroutine 中調用時，總共會延遲 6 秒。
![CleanShot 2024-08-24 at 23.30.38](https://hackmd.io/_uploads/rk5jhuPsC.png)

![CleanShot 2024-08-24 at 23.30.31](https://hackmd.io/_uploads/ryBsh_woA.png)

# Coroutine Contexts
## Coroutine Context 簡介
- Coroutine 是在特定的上下文（Context）中啟動的，這個上下文決定了 Coroutine 會在哪個執行緒中運行。
- 之前我們只使用了 `GlobalScope.launch` 來啟動 Coroutine，這沒有給予我們太多的控制。

## 使用 Dispatchers 控制 Coroutine 的執行緒
![CleanShot 2024-08-24 at 23.33.09](https://hackmd.io/_uploads/rybBTuDjR.png)
| **Dispatcher**           | **Dispatchers.Main**                                      | **Dispatchers.IO**                                      | **Dispatchers.Default**                                     | **Dispatchers.Unconfined**                                    |
|--------------------------|----------------------------------------------------------|---------------------------------------------------------|-------------------------------------------------------------|----------------------------------------------------------------|
| **特性**                  | - 主要在 Android 中使用<br>- 使用 UI 線程執行              | - 用於 I/O 密集型任務<br>- 具有比 Default 更高的線程池數量 | - 用於 CPU 密集型任務<br>- 線程池數量與 CPU 核心數相當        | - 不限於特定線程<br>- 根據掛起函數的繼續位置決定在哪個線程運行 |
| **適用場景**              | - 更新 UI 元素<br>- 處理與用戶交互相關的邏輯              | - 網路請求<br>- 檔案讀寫<br>- 資料庫操作                 | - 複雜的計算任務<br>- 資料處理<br>- 大型數據操作              | - 只適合簡單任務<br>- 通常用於測試或一些特殊場景                |
| **應用範例 1**            | **即時天氣應用**<br>- 從 API 獲取天氣資料後，使用 `Dispatchers.Main` 將資料顯示在 UI 上，並更新天氣狀態。 | **即時天氣應用**<br>- 從 API 獲取天氣資料（如溫度、濕度）並保存到本地資料庫，使用 `Dispatchers.IO` 處理。 | **即時天氣應用**<br>- 根據多天的天氣數據進行計算，預測未來幾天的天氣趨勢和降水概率，使用 `Dispatchers.Default` 處理這些複雜計算。 | **即時天氣應用**<br>- 在開發過程中快速驗證 UI 相關行為的執行，使用 `Dispatchers.Unconfined` 進行測試。 |
| **應用範例 2**            | **新聞閱讀器應用**<br>- 在加載完新聞內容後，使用 `Dispatchers.Main` 更新 UI 以顯示文章和圖片。 | **新聞閱讀器應用**<br>- 從遠程伺服器下載最新的新聞文章並儲存到本地緩存，使用 `Dispatchers.IO` 處理。 | **新聞閱讀器應用**<br>- 解析新聞資料，根據用戶的興趣生成個性化新聞推送，使用 `Dispatchers.Default` 處理這些計算。 | **新聞閱讀器應用**<br>- 在開發過程中進行快速原型設計，使用 `Dispatchers.Unconfined` 來測試文章顯示邏輯。 |
| **應用範例 3**            | **健身追蹤應用**<br>- 當用戶完成運動後，使用 `Dispatchers.Main` 更新運動數據到 UI，顯示卡路里消耗等資訊。 | **健身追蹤應用**<br>- 從穿戴式設備獲取用戶的運動數據（如步數和心率），並將這些數據儲存到本地資料庫，使用 `Dispatchers.IO` 處理。 | **健身追蹤應用**<br>- 分析用戶的運動數據，生成運動報告和建議，使用 `Dispatchers.Default` 處理這些資料分析和計算。 | **健身追蹤應用**<br>- 在開發過程中快速測試不同運動算法的效果，使用 `Dispatchers.Unconfined` 進行實驗。 |

- 我們可以透過傳遞 `Dispatcher` 參數給 `launch` 函數來控制 Coroutine 的執行緒。
- 常用的 Dispatchers 包括：
  - `Dispatchers.Main`: 在主執行緒中啟動 Coroutine，適合進行 UI 操作，因為只能從主執行緒更改 UI。
  - `Dispatchers.IO`: 用於執行資料操作，例如網路請求、資料庫操作或檔案讀寫。
  - `Dispatchers.Default`: 適合執行複雜且長時間運行的計算，避免阻塞主執行緒和 UI。
  - `Dispatchers.Unconfined`: 不受限於特定執行緒，Coroutine 會在上次暫停後恢復的執行緒中繼續運行。
  - `newSingleThreadContext`: 可以創建一個新的執行緒並在其中運行 Coroutine。
![CleanShot 2024-08-24 at 23.55.12](https://hackmd.io/_uploads/BJpDzYwi0.png)

## 在 Coroutine 中切換上下文
- Coroutine 的強大之處在於可以輕鬆地在不同的上下文之間切換。
- 例如，我們可以在 IO Dispatcher 中執行網路請求，然後切換到 Main Dispatcher 來更新 UI。

## 例子：網路請求與 UI 更新
![CleanShot 2024-08-24 at 23.57.17](https://hackmd.io/_uploads/ByKyQFDjR.png)
- 在 IO Dispatcher 中執行網路請求並獲取結果：
  ```kotlin
  val answer = doNetworkCall()
  ```
- 然後使用 `withContext` 切換到 Main Dispatcher 來更新 UI：
  ```kotlin
  withContext(Dispatchers.Main) {
      tvDummy.text = answer
  }
  ```
- 使用 `Log` 來檢查 Coroutine 執行緒的切換：
  - 在 IO Dispatcher 中記錄當前執行緒：
    ```kotlin
    Log.d("MainActivity", "Starting coroutine in thread: ${Thread.currentThread().name}")
    ```
  - 在 Main Dispatcher 中記錄更新 UI 的執行緒：
    ```kotlin
    Log.d("MainActivity", "Setting text in thread: ${Thread.currentThread().name}")
    ```

## 執行結果
- 當應用運行時，可以看到在不同的執行緒中分別執行網路請求和 UI 更新操作，確認 Coroutine Context 的有效性。

# runBlocking
## `runBlocking` 函數介紹
- `runBlocking` 函數會在主執行緒中啟動一個 Coroutine 並且會阻塞主執行緒。
- 與 `GlobalScope.launch(Dispatchers.Main)` 不同，`runBlocking` 會完全阻塞主執行緒，使 UI 更新也無法進行。

## 使用情境
- 使用 `runBlocking` 可以在主執行緒中執行 Suspend 函數，而不需要異步執行。
- 在測試中可以使用 `runBlocking` 來同步執行 Suspend 函數。
- 適合用來快速測試或了解 Coroutine 的行為。

## `runBlocking` 的執行流程
- 在 `runBlocking` 中調用 `delay` 函數，會阻塞主執行緒，類似於使用 `Thread.sleep`。
- 可以在 `runBlocking` 中使用 Suspend 函數，因為它提供了 Coroutine Scope。

## 範例與日誌驗證
![CleanShot 2024-08-25 at 00.27.07](https://hackmd.io/_uploads/By7e5YDj0.png)

- 在 `runBlocking` 區塊前後添加日誌，並在區塊內部與外部調用 `delay` 進行比較：
  ```kotlin
  Log.d("MainActivity", "Before runBlocking")
  runBlocking {
      Log.d("MainActivity", "Start of runBlocking")
      delay(5000)
      Log.d("MainActivity", "End of runBlocking")
  }
  Log.d("MainActivity", "After runBlocking")
  ```
- 執行結果會顯示：
  - `Before runBlocking`
  - `Start of runBlocking`
  - 延遲 5 秒
  - `End of runBlocking`
  - `After runBlocking`
  
- 移除 `runBlocking` 並改用 `Thread.sleep` 也會得到相同結果，但無法在 `Thread.sleep` 中調用 Suspend 函數。

## 在 `runBlocking` 中啟動 Coroutine
![CleanShot 2024-08-25 at 00.28.32](https://hackmd.io/_uploads/SyCV9KwiR.png)

- 可以在 `runBlocking` 中啟動新的 Coroutine，這些 Coroutine 不會被阻塞，並且可以使用不同的 Dispatcher。
- 例子：
  ```kotlin
  runBlocking {
      launch(Dispatchers.IO) {
          delay(3000)
          Log.d("MainActivity", "Finished IO coroutine 1")
      }
      launch(Dispatchers.IO) {
          delay(3000)
          Log.d("MainActivity", "Finished IO coroutine 2")
      }
      Log.d("MainActivity", "End of main thread coroutine")
  }
  ```
- 結果顯示兩個 IO Coroutine 同時結束，並在主執行緒的 Coroutine 結束後打印結果。

# Jobs, Waiting, Cancelation
## Coroutine 中的 Job 介紹
- 啟動 Coroutine 時會返回一個 `Job`。
- `Job` 可用於管理 Coroutine，如等待完成或取消。

## 等待 Coroutine 完成
- 使用 `job.join()` 等待 Coroutine 完成，阻塞當前執行緒。
- 可以在 `runBlocking` 中使用 `job.join()`。

## 範例：使用 Job 等待 Coroutine 完成
- 啟動 Coroutine 並使用 `job.join()` 等待其完成。
- 在 Coroutine 完成後再繼續執行後續程式碼。
![CleanShot 2024-08-25 at 00.32.27](https://hackmd.io/_uploads/SkKQsYvjR.png)

![CleanShot 2024-08-25 at 00.31.37](https://hackmd.io/_uploads/rkpgoKPoA.png)

## 取消 Coroutine 的 Job
- 使用 `job.cancel()` 來取消正在運行的 Coroutine。
- 在取消後，Coroutine 內部未完成的工作將不再執行。
![CleanShot 2024-08-25 at 00.32.38](https://hackmd.io/_uploads/BJ7VstPoA.png)
![CleanShot 2024-08-25 at 00.33.03](https://hackmd.io/_uploads/ryQIoFPiA.png)

## 協作式的 Coroutine 取消
- 取消 Coroutine 是協作式的，Coroutine 必須檢查取消狀態。
- 如果 Coroutine 執行長時間運算，需手動檢查是否被取消。
![CleanShot 2024-08-25 at 00.34.18](https://hackmd.io/_uploads/By6qjYwoR.png)
![CleanShot 2024-08-25 at 00.34.41](https://hackmd.io/_uploads/HJ0iiFviC.png)
## 範例：手動檢查 Coroutine 取消狀態
![CleanShot 2024-08-25 at 00.35.26](https://hackmd.io/_uploads/H1iRjFPjR.png)
- 當 Coroutine 執行長時間計算時，使用 `isActive` 檢查是否被取消。

## 使用 `withTimeout` 設定超時
![CleanShot 2024-08-25 at 00.36.18](https://hackmd.io/_uploads/HkzGntwi0.png)

- `withTimeout` 用於設置 Coroutine 的超時時間。
- 若 Coroutine 運行超過指定時間，將自動取消。


# Async and Await
## `async` 與 `await` 函數介紹
- `async` 和 `await` 用於在 Coroutine 中執行多個異步操作。
- 預設情況下，Coroutine 中的多個 `suspend` 函數是順序執行的。

## 範例：順序執行的問題
![CleanShot 2024-08-25 at 00.40.44](https://hackmd.io/_uploads/ByFfaYwsA.png)

- 當我們執行兩個網路請求時，預設它們會一個接一個地執行，導致總耗時累加。
- 在這個範例中，每個網路請求需要 3 秒，所以總共需要 6 秒。

## 測量執行時間
- 使用 `measureTimeMillis` 來測量一段代碼的執行時間。
![CleanShot 2024-08-25 at 00.41.06](https://hackmd.io/_uploads/BykVpKvsR.png)

## 問題解決：使用 `async` 來異步執行
![CleanShot 2024-08-25 at 00.41.37](https://hackmd.io/_uploads/rkzI6KPsC.png)
![CleanShot 2024-08-25 at 00.41.52](https://hackmd.io/_uploads/HkhLaYPiR.png)
![CleanShot 2024-08-25 at 00.42.45](https://hackmd.io/_uploads/ByfcTtvsC.png)

- 使用 `async` 啟動新的 Coroutine，允許兩個網路請求同時執行，縮短總耗時。
- `async` 返回一個 `Deferred` 對象，可用於取得計算結果。
- 使用 `await` 函數來等待 `Deferred` 的結果，而不會阻塞其他操作。

## 壞習慣的示範與優化
- 壞習慣：使用多個 `launch` 來啟動 Coroutine，並使用 `join` 來等待它們完成。
- 優化：使用 `async` 取代 `launch`，並用 `await` 來獲取結果，這種方式更簡潔且有效。

## 注意事項
- 當需要 Coroutine 返回結果時，應使用 `async` 而非 `launch`。
- 若 Coroutine 無需返回結果，則應使用 `launch`。

# lifecycleScope and viewModelScope
## Coroutine Scopes 簡介
- `GlobalScope` 用於啟動一個與應用程式生命週期相同的 Coroutine，但通常不建議使用，因為大多數情況下不需要 Coroutine 活到應用程式結束。
- 在 Android 中，有兩個非常實用的預定義 Coroutine 範圍：`LifecycleScope` 和 `ViewModelScope`。
![CleanShot 2024-08-25 at 00.57.28](https://hackmd.io/_uploads/H1Ob-cDi0.png)

## `LifecycleScope` 介紹
- `LifecycleScope` 允許 Coroutine 隨著 Activity 或 Fragment 的生命週期而管理。
- 當 Activity 或 Fragment 被銷毀時，所有在該 `LifecycleScope` 中啟動的 Coroutine 也會被自動取消。

## 範例：避免使用 `GlobalScope` 的問題
![CleanShot 2024-08-25 at 00.58.40](https://hackmd.io/_uploads/S10rW9wiC.png)

- 使用 `GlobalScope` 啟動 Coroutine 會導致即使 Activity 被銷毀，Coroutine 仍然繼續執行，這可能導致記憶體洩漏。
- 若 Coroutine 使用了已被銷毀的 Activity 的資源，這些資源將無法被垃圾回收。

## 修正：使用 `LifecycleScope`
![CleanShot 2024-08-25 at 00.59.11](https://hackmd.io/_uploads/ryRwb5viC.png)

- 將 `GlobalScope` 替換為 `LifecycleScope` 可以避免上述問題。
- 在 `LifecycleScope` 中啟動的 Coroutine 會隨著 Activity 被銷毀而自動取消，避免資源泄漏。

## `ViewModelScope` 介紹
- `ViewModelScope` 允許 Coroutine 隨著 ViewModel 的生命週期而管理。
- 與 `LifecycleScope` 類似，但它會讓 Coroutine 保持存活直到 ViewModel 被清除。

## 注意事項
- 若 Coroutine 執行長時間的運算且不暫停，應定期檢查是否仍處於活動狀態，以便能夠正確取消。
- `LifecycleScope` 也適用於 Fragment 的生命週期管理。

# Coroutines with Firebase Firestore
## Coroutine 與 Firebase Firestore 的結合
- 當使用 Firebase Firestore 時，經常會遇到所謂的「回調地獄」（callback hell），這是由於多個網路操作相互依賴，並且依賴回調函數來獲取數據。
- Coroutine 可以幫助解決這個問題，因為它們能夠暫停並恢復執行，不需要依賴回調函數。
![CleanShot 2024-08-25 at 01.19.49](https://hackmd.io/_uploads/rkaSL5Ps0.png)

## 範例：使用 Coroutine 儲存與讀取資料
- 要將 Coroutine 與 Firestore 結合使用，需要加入 `kotlinx-coroutines-play-services` 依賴。
- 透過 Coroutine，Firestore 的操作變得更為簡潔與直觀。
![CleanShot 2024-08-25 at 01.20.33](https://hackmd.io/_uploads/rkTv8cPj0.png)

## 使用 Coroutine 儲存資料
- 在 Coroutine 中使用 `set` 函數將資料儲存到 Firestore 文件中，並使用 `await` 來等待操作完成。
- `await` 是一個 suspend 函數，會阻塞 Coroutine 直到操作完成。
![CleanShot 2024-08-25 at 01.22.08](https://hackmd.io/_uploads/ryl0UqDj0.png)
![CleanShot 2024-08-25 at 01.22.24](https://hackmd.io/_uploads/SJoR8qwj0.png)

## 使用 Coroutine 讀取資料
- 使用 `get` 函數來獲取 Firestore 文件中的資料，並使用 `await` 等待資料返回。
![CleanShot 2024-08-25 at 01.22.41](https://hackmd.io/_uploads/rJT1P5voC.png)
- 將返回的資料轉換為 Kotlin 對象後，透過 `withContext` 切換到主線程更新 UI。
![CleanShot 2024-08-25 at 01.23.21](https://hackmd.io/_uploads/Hk8Gw9viR.png)

## 範例總結
- Coroutine 讓 Firestore 的異步操作更加容易且避免了回調地獄。
![CleanShot 2024-08-25 at 01.23.53](https://hackmd.io/_uploads/SyKVD5Ds0.png)

- Coroutine 具備生命週期感知功能，可以避免在 Activity 被銷毀後仍然執行異步操作。

## 其他注意事項
- 在使用 Coroutine 處理 Firestore 操作時，確保在合適的上下文中執行，以避免不必要的資源佔用。

# Coroutines with Retrofit
## Retrofit 與 Coroutine 的結合
- 傳統上，我們使用 `enqueue` 來進行 Retrofit 網路請求，這會在一個新的執行緒中執行請求並透過回調通知結果。
- 這種方式雖然有效，但效率較低，因為會啟動一個新的執行緒。
![CleanShot 2024-08-25 at 01.26.45](https://hackmd.io/_uploads/rJGydcPjA.png)

## 使用 Coroutine 進行網路請求
- 使用 Coroutine 可以避免啟動額外的執行緒，代碼更簡潔高效。
- 可以在 Coroutine 中使用 `await` 來等待請求結果，並直接獲取資料。
![CleanShot 2024-08-25 at 01.27.36](https://hackmd.io/_uploads/SJ4zu5Do0.png)

## 範例：使用 Coroutine 獲取資料
- 在 Coroutine 中使用 `await` 來等待 Retrofit 請求並獲取結果，將結果直接用於後續操作。
- 如果需要處理請求錯誤，可以使用 `awaitResponse` 來獲取整個 `Response` 對象，並檢查請求是否成功。
![CleanShot 2024-08-25 at 01.28.04](https://hackmd.io/_uploads/HymVO5vjR.png)
![CleanShot 2024-08-25 at 01.28.14](https://hackmd.io/_uploads/r1s4d5viC.png)

## 優化方式：直接使用 Suspend 函數
- 可以在 Retrofit 的 API 介面中將函數定義為 `suspend` 函數，直接返回資料或 `Response` 對象，避免額外的 `Call` 包裝。
- 使用 `suspend` 函數後，Coroutine 會自動處理異步操作，代碼更簡潔，易於維護。
![CleanShot 2024-08-25 at 01.28.22](https://hackmd.io/_uploads/By4rOqvjR.png)

## 總結
- 結合 Coroutine 與 Retrofit 不僅能減少代碼量，還能提升應用程式的執行效率。
- 直接使用 `suspend` 函數讓網路請求的處理更加直觀，且簡化了錯誤處理的邏輯。


# In-Depth Guide to Coroutine Cancellation & Exception Handling
## Coroutine 中的例外處理與取消機制
- Coroutine 是處理非同步程式設計的強大工具，但當深入了解時，會發現許多陷阱，特別是在例外處理和取消機制方面。
![CleanShot 2024-08-25 at 02.00.50](https://hackmd.io/_uploads/ByiJgjDiC.png)

## 基本例外處理的誤解
- 許多人認為可以直接使用 `try-catch` 區塊來捕捉 Coroutine 中的例外，但這通常無效。
![CleanShot 2024-08-25 at 02.01.33](https://hackmd.io/_uploads/SJKzgiPo0.png)

- 如果例外未在 Coroutine 中直接處理（即未在引發例外的 Coroutine 中使用 `try-catch`），則例外將被傳遞到父 Coroutine，最終導致應用程序崩潰。
![CleanShot 2024-08-25 at 02.02.37](https://hackmd.io/_uploads/HJiBgswiA.png)

## Coroutine Scope 的結構
- Coroutine Scope（例如 `LifecycleScope`、`ViewModelScope`）通常會啟動一個 Coroutine，而這個 Coroutine 可以啟動子 Coroutine。
- 如果子 Coroutine 中發生未捕捉的例外，該例外將被向上傳遞到父 Coroutine，直到外層 Coroutine 捕捉該例外或應用程序崩潰。

## 例外傳遞與取消
- 當 Coroutine 被取消時，會引發 `CancellationException`，這並不會導致應用崩潰，但會被傳遞到所有相關的 Coroutine 中，以通知它們已被取消。
- 例外處理與取消機制類似，例外或取消狀態會向上傳遞，直到被適當處理。

## 使用 `launch` 與 `async` 的差異
- `launch` 用於啟動一個不需要返回值的 Coroutine，而 `async` 用於啟動一個需要返回值的 Coroutine。
![CleanShot 2024-08-25 at 02.04.00](https://hackmd.io/_uploads/HyKsgoPsA.png)

- `async` 會返回一個 `Deferred`，可以使用 `await` 來獲取結果或捕捉例外。
- 如果 `async` 中發生例外，例外不會立即拋出，而是在調用 `await` 時才拋出。

## `async` 中的例外處理
![CleanShot 2024-08-25 at 02.04.39](https://hackmd.io/_uploads/SJSplsvs0.png)
- 如果在 `async` 中發生例外，例外會在調用 `await` 時被拋出，而不是立即拋出。
- 如果 `async` 的結果未被等待（未調用 `await`），則不會立即發生崩潰。
- 在調用 `await` 時，可以使用 `try-catch` 來捕捉例外。
![CleanShot 2024-08-25 at 02.05.28](https://hackmd.io/_uploads/r18x-jPoR.png)
![CleanShot 2024-08-25 at 02.05.37](https://hackmd.io/_uploads/H1AeZjPjC.png)

## 錯誤的例外處理方式
- 雖然可以在 `await` 語句中使用 `try-catch` 捕捉例外，但這並不是推薦的做法。
- 存在更好的方式來處理 Coroutine 中的例外與取消機制，應避免直接在 `await` 語句中使用 `try-catch`。 

## Coroutine 中的例外處理與 `CoroutineExceptionHandler`
![CleanShot 2024-08-25 at 03.13.56](https://hackmd.io/_uploads/SyyzZ3voR.png)
![CleanShot 2024-08-25 at 03.14.17](https://hackmd.io/_uploads/BkVGW2viA.png)
![CleanShot 2024-08-25 at 03.14.57](https://hackmd.io/_uploads/rJfSWhvsC.png)
![CleanShot 2024-08-25 at 03.15.15](https://hackmd.io/_uploads/BkJ8WhDo0.png)

- 當我們想處理未捕捉的例外時，可以使用 `try-catch` 區塊，但必須在引發例外的 Coroutine 內部處理，或在 `async` 的 `await` 調用時處理。
- 另一種方法是使用 `CoroutineExceptionHandler`，這是一個特定的處理器，能夠處理未捕捉的例外。

## 使用 `CoroutineExceptionHandler` 的例外處理
- 可以在 Coroutine 的根範圍內安裝 `CoroutineExceptionHandler`，以捕捉所有從子 Coroutine 傳遞上來的未捕捉例外。
- 這種方式不會處理取消例外 (`CancellationException`)，因為取消例外是由 Coroutine 內部處理的，並不會導致應用崩潰。

## Coroutine 範圍：`CoroutineScope` 與 `SupervisorScope`
- 在 Coroutine 中，有兩種範圍決定子 Coroutine 的取消方式：`CoroutineScope` 和 `SupervisorScope`。
- `CoroutineScope`：當任何一個子 Coroutine 發生例外時，會取消所有的子 Coroutine 以及整個範圍。
- `SupervisorScope`：當一個子 Coroutine 發生例外時，其他子 Coroutine 不會受到影響，繼續執行。

## 範例：`CoroutineScope` 對比 `SupervisorScope`
- 使用 `CoroutineScope` 時，若一個子 Coroutine 失敗，所有子 Coroutine 都會被取消，即使已經捕捉了例外。
![CleanShot 2024-08-25 at 03.16.42](https://hackmd.io/_uploads/B1PhZnwsC.png)
![CleanShot 2024-08-25 at 03.17.09](https://hackmd.io/_uploads/By-6-2DoA.png)
- 使用 `SupervisorScope` 時，若一個子 Coroutine 失敗，其他子 Coroutine 仍會繼續執行，不會因例外被取消。
![CleanShot 2024-08-25 at 03.17.37](https://hackmd.io/_uploads/Skykf3viC.png)

| 特性                     | CoroutineScope                                 | SupervisorScope                                |
|--------------------------|------------------------------------------------|------------------------------------------------|
| 子協程失敗處理            | 如果任何一個子協程失敗，會取消整個範圍 (Scope) 並取消所有子協程。 | 失敗的子協程不會影響其他子協程或協程範圍 (Scope)，失敗只會影響該協程。 |
| 適用情境                 | 當多個子協程相互依賴且需要所有協程都成功時。     | 當多個子協程彼此獨立且不希望一個協程失敗影響其他協程時。             |
| 使用場合                 | 需要管理相依的工作流程，如其中一個失敗需要全部取消。 | 需要管理獨立的工作流程，如其中一個失敗不影響整體運作。               |
| 範圍終止                 | 當任何一個子協程失敗時，會取消整個範圍。        | 只有在範圍中的所有協程都完成或被手動取消時，範圍才會終止。            |
| 範例一：資料同步 App      | 同步用戶資料到多個服務器，若任一伺服器同步失敗，需取消所有同步操作。 | 下載多個伺服器的資料，如果一個伺服器下載失敗，不影響其他伺服器的下載。|
| 範例二：影片處理 App      | 合併多段影片，如果其中一段合併失敗，應取消整個合併過程。 | 轉換多段影片格式，如果其中一段轉換失敗，其他段落繼續轉換。           |
| 範例三：社交媒體 App      | 發送多個通知至不同平台，若任一平台發送失敗，取消所有發送操作。 | 從多個社交平台拉取消息，若一個平台失敗，不影響其他平台的消息拉取。    |


## 常見錯誤：不正確的例外處理
- 在 Coroutine 中使用 `try-catch` 捕捉例外時，若捕捉了 `CancellationException`，這會導致外層範圍無法感知到取消，進而繼續執行已被取消的 Coroutine。
![CleanShot 2024-08-25 at 03.42.47](https://hackmd.io/_uploads/HyS6vhDj0.png)
![CleanShot 2024-08-25 at 03.43.16](https://hackmd.io/_uploads/r1by_3Ps0.png)
- 修正方法：要麼僅捕捉特定的例外（如 `HttpException`），要麼在捕捉一般例外時，檢查是否為 `CancellationException`，並重新拋出它。
![CleanShot 2024-08-25 at 03.44.11](https://hackmd.io/_uploads/B1vM_2DiA.png)
![CleanShot 2024-08-25 at 03.44.34](https://hackmd.io/_uploads/H16XuhDiR.png)
![CleanShot 2024-08-25 at 03.44.43](https://hackmd.io/_uploads/HJKN_nDjC.png)

## 深入理解與資源浪費問題
- 當 Coroutine 被取消時，若沒有正確處理取消例外，可能導致子 Coroutine 繼續執行，造成不必要的資源消耗。
- 通常，我們希望在取消發生時，立即終止 Coroutine 的執行，以避免資源浪費。 

# [協程的例外處理](https://hackmd.io/@RainBowT/ryuTWjvjR)

# Terminology
- **Kotlin**：一種現代化、靜態類型的程式語言，由 JetBrains 開發，並且是 Android 開發的官方支援語言。
- **協程 (Coroutine)**：Kotlin 中一種輕量級的執行單位，類似於執行緒 (Thread)，但更加靈活和高效。
- **函數 (Function)**：一組可重用的程式碼指令，接受輸入參數並返回輸出結果。
- **執行緒 (Thread)**：一個程序內部的執行單位，每個執行緒都可以同時執行指令。
- **主執行緒 (Main Thread)**：Android 應用中負責處理 UI 更新的主要執行緒。
- **多執行緒 (Multi-threading)**：同時執行多個執行緒的技術，可以讓程式同時處理多個任務。
- **UI 更新 (UI Update)**：在 Android 中，更新使用者界面元素的過程，通常在主執行緒中進行。
- **網路請求 (Network Call)**：應用程式向伺服器發送請求並等待回應的過程。
- **暫停 (Suspend)**：協程中可以暫時停止執行並稍後繼續的一種功能。
- **上下文切換 (Context Switching)**：協程可以從一個執行緒切換到另一個執行緒繼續執行的過程。
- **輕量級 (Lightweight)**：形容協程佔用的系統資源極少，允許大量同時執行。
- **記憶體不足錯誤 (Out of Memory Error)**：當系統無法再分配記憶體給新執行緒時發生的錯誤。
- **建構工地 (Construction Site)**：影片中的比喻，用來表示執行緒的工作環境。
- **小工人 (Little Worker)**：影片中的比喻，用來表示協程在執行緒中的工作單位。
- **指令 (Instruction)**：程式碼中的基本操作步驟。
- **平行運行 (Run in Parallel)**：多個執行緒或協程同時執行的過程。
- **程序 (Program)**：一個電腦程式的整體結構，包括所有執行緒和指令。
- **暫停執行 (Pause Execution)**：暫時停止協程的運行，並在稍後繼續。
- **程式設計師 (Programmer)**：控制協程和執行緒行為的開發者。
- **Android 應用 (Android App)**：一個在 Android 平台上運行的應用程式。
- **依賴項 (Dependencies)**：在專案中必須包含的外部庫或模組，以便使用特定功能。
- **build.gradle**：一個用於配置 Android 專案的檔案，其中定義了專案的依賴項、插件等。
- **Global Scope**：Kotlin 中的一個協程範圍，協程在這個範圍中會與應用程式的生命週期一致。
- **launch**：啟動一個新的協程，並在指定的範圍內執行一系列指令。
- **協程範圍 (Coroutine Scope)**：協程的執行上下文，決定協程的生命週期和執行範圍。
- **異步執行 (Asynchronously Execute)**：在不阻塞主執行緒的情況下執行任務。
- **Log.d**：用於在 Android 應用中輸出除錯訊息的函數。
- **tag**：在 Android 中標識除錯訊息來源的標籤，通常是類名或模組名。
- **執行緒名稱 (Thread Name)**：表示執行緒的名稱，用於辨識不同的執行緒。
- **主執行緒 (Main Thread)**：應用程式的主執行緒，負責處理 UI 更新和主要邏輯。
- **預設分派器 (Default Dispatcher)**：Kotlin 協程中的預設執行緒池，負責協程的執行。
- **延遲 (Delay)**：Kotlin 協程中的函數，用於暫停協程一段時間而不阻塞執行緒。
- **暫停 (Pause)**：暫時停止協程的執行，稍後可繼續。
- **執行緒阻塞 (Thread Blocking)**：當執行緒停止執行並等待某個事件或操作完成時的狀態。
- **sleep**：一個執行緒方法，用於暫停執行緒的執行一段時間。
- **Lockcat**：Android 中的日誌系統，用於查看應用程式的輸出訊息和錯誤。
- **預設分派器工人 (Default Dispatcher Worker)**：在預設分派器中執行工作的執行緒。
- **建構工地 (Construction Site)**：影片中的比喻，用於表示執行緒的工作環境。
- **應用程式生命週期 (Application Lifecycle)**：應用程式從啟動到結束的整個過程。
- **取消協程 (Cancel Coroutine)**：當協程範圍結束或條件觸發時，終止協程的執行。
- **協程 (Coroutine)**：Kotlin 中的輕量級執行單位，可以在一個執行緒內運行多個協程。
- **延遲 (Delay)**：協程中的一個函數，用於暫停協程執行一段時間而不阻塞執行緒。
- **全域範圍 (Global Scope)**：Kotlin 中協程的一個範圍，協程在這個範圍中會與應用程式的生命週期一致。
- **掛起函數 (Suspend Function)**：Kotlin 中一種特殊的函數，只有在協程或其他掛起函數內才能執行。
- **掛起 (Suspend)**：暫時停止協程的執行，等待特定條件達成後繼續執行。
- **網路請求 (Network Call)**：應用程式向伺服器發送請求並等待回應的過程。
- **字串 (String)**：程式設計中的一種資料型態，表示文字資料。
- **返回值 (Return Value)**：函數執行後返回的結果。
- **延遲函數 (Delay Function)**：掛起函數的一種，用於延遲協程的執行。
- **Android Studio**：Google 推出的官方集成開發環境 (IDE)，用於 Android 應用程式的開發。
- **錯誤 (Error)**：程式執行過程中發生的問題，可能會導致程式停止運行。
- **執行緒 (Thread)**：一個程序內部的執行單位，每個執行緒都可以同時執行指令。
- **模擬 (Simulate)**：在程式設計中，用人為設置的環境或條件來模擬真實情況。
- **印出 (Print)**：將資料輸出到控制台或日誌系統，用於除錯或記錄。
- **日誌 (Log)**：記錄程式執行過程中產生的訊息，用於分析和除錯。
- **重複 (Duplicate)**：複製程式碼片段，使其多次出現在不同的地方。
- **掛起箭頭 (Suspend Arrow)**：在 Android Studio 中，標記掛起函數的小箭頭圖示。
- **錯誤訊息 (Error Message)**：程式發生錯誤時顯示的訊息，用於幫助開發者解決問題。
- **主執行緒 (Main Thread)**：應用程式的主執行緒，負責處理 UI 更新和主要邏輯。
- **程式模擬 (Program Simulation)**：在開發過程中，用假資料或條件模擬程式的真實運行情況。
- **協程上下文 (Coroutine Context)**：定義協程在哪個執行緒或執行環境中運行的配置。
- **分派器 (Dispatcher)**：Kotlin 協程中的一個組件，決定協程應該在哪個執行緒或執行環境中執行。
- **Global Scope**：Kotlin 中的一個協程範圍，協程在這個範圍中會與應用程式的生命週期一致。
- **Dispatchers.Main**：分派器的一種，指定協程在主執行緒（UI 執行緒）中運行，適合用於更新 UI。
- **Dispatchers.IO**：分派器的一種，適合用於執行 I/O 密集型操作，如網路請求、文件讀寫等。
- **Dispatchers.Default**：分派器的一種，適合用於執行 CPU 密集型操作，如複雜計算或長時間運行的任務。
- **Dispatchers.Unconfined**：分派器的一種，協程在任何當前的執行緒中運行，而不會固定在特定的執行緒上。
- **新單一執行緒上下文 (New Single Thread Context)**：創建一個新的執行緒，並將協程運行在該執行緒中。
- **withContext**：Kotlin 中的一個函數，用於在協程內部切換上下文，讓協程在不同的執行環境中執行。
- **延遲函數 (Delay Function)**：掛起函數的一種，用於暫停協程的執行。
- **I/O 密集型操作 (I/O Intensive Operation)**：涉及大量輸入輸出操作的任務，如網路請求和文件讀寫。
- **CPU 密集型操作 (CPU Intensive Operation)**：涉及大量計算或處理的任務，如數據處理或複雜算法運行。
- **掛起函數 (Suspend Function)**：Kotlin 中的一種特殊函數，只有在協程或其他掛起函數內才能執行。
- **主執行緒 (Main Thread)**：應用程式的主要執行緒，負責處理 UI 更新和主要邏輯。
- **執行緒名稱 (Thread Name)**：用於辨識不同執行緒的名稱。
- **鎖定日誌 (Logcat)**：Android 中的日誌系統，用於查看應用程式的輸出訊息和錯誤。
- **UI 操作 (UI Operation)**：更新或操作使用者介面元素的過程，通常需要在主執行緒中執行。
- **上下文切換 (Context Switching)**：在協程中切換執行環境或執行緒的過程。
- **假設網路請求 (Simulated Network Call)**：模擬一個網路請求，用於測試或演示。
- **UI 元素 (UI Element)**：使用者介面中的單個組件，如按鈕、文字框等。
- **協程 (Coroutine)**：Kotlin 中的輕量級執行單位，可以在一個執行緒內運行多個協程。
- **掛起函數 (Suspend Function)**：Kotlin 中一種特殊的函數，只有在協程或其他掛起函數內才能執行。
- **延遲 (Delay)**：協程中的一個函數，用於暫停協程執行一段時間而不阻塞執行緒。
- **全域範圍 (Global Scope)**：Kotlin 中的一個協程範圍，協程在這個範圍中會與應用程式的生命週期一致。
- **Dispatchers.Main**：分派器的一種，指定協程在主執行緒（UI 執行緒）中運行，適合用於更新 UI。
- **執行緒阻塞 (Thread Blocking)**：當執行緒停止執行並等待某個事件或操作完成時的狀態。
- **主執行緒 (Main Thread)**：應用程式的主要執行緒，負責處理 UI 更新和主要邏輯。
- **runBlocking**：Kotlin 中的一個函數，啟動一個新的協程並阻塞其所處的執行緒，直到協程執行完成。
- **非同步執行 (Asynchronous Execution)**：在不阻塞主執行緒的情況下執行任務，允許多個任務同時進行。
- **jUnit**：一個常用的 Java 單元測試框架，用於測試程式碼。
- **延遲函數 (Delay Function)**：掛起函數的一種，用於延遲協程的執行。
- **Logcat**：Android 中的日誌系統，用於查看應用程式的輸出訊息和錯誤。
- **執行緒名稱 (Thread Name)**：用於辨識不同執行緒的名稱。
- **IO 密集型操作 (I/O Intensive Operation)**：涉及大量輸入輸出操作的任務，如網路請求和文件讀寫。
- **CPU 密集型操作 (CPU Intensive Operation)**：涉及大量計算或處理的任務，如數據處理或複雜算法運行。
- **協程上下文 (Coroutine Context)**：定義協程在哪個執行緒或執行環境中運行的配置。
- **分派器 (Dispatcher)**：Kotlin 協程中的一個組件，決定協程應該在哪個執行緒或執行環境中執行。
- **上下文切換 (Context Switching)**：在協程中切換執行環境或執行緒的過程。
- **日誌訊息 (Log Message)**：用於記錄程式執行過程中的事件或狀態訊息。
- **同步執行 (Synchronous Execution)**：任務按順序執行，每個任務必須等待前一個任務完成。
- **UI 操作 (UI Operation)**：更新或操作使用者介面元素的過程，通常需要在主執行緒中執行。
- **協程 (Coroutine)**：Kotlin 中的輕量級執行單位，可以在一個執行緒內運行多個協程。
- **掛起函數 (Suspend Function)**：Kotlin 中一種特殊的函數，只有在協程或其他掛起函數內才能執行。
- **延遲 (Delay)**：協程中的一個函數，用於暫停協程執行一段時間而不阻塞執行緒。
- **全域範圍 (Global Scope)**：Kotlin 中的一個協程範圍，協程在這個範圍中會與應用程式的生命週期一致。
- **Dispatchers.Default**：分派器的一種，適合用於執行 CPU 密集型操作，如複雜計算或長時間運行的任務。
- **主執行緒 (Main Thread)**：應用程式的主要執行緒，負責處理 UI 更新和主要邏輯。
- **runBlocking**：Kotlin 中的一個函數，啟動一個新的協程並阻塞其所處的執行緒，直到協程執行完成。
- **非同步執行 (Asynchronous Execution)**：在不阻塞主執行緒的情況下執行任務，允許多個任務同時進行。
- **Job**：協程啟動後返回的控制單位，可以用來監控和控制協程的執行狀態。
- **join**：協程中的一個掛起函數，用來等待協程完成執行。
- **cancel**：用於取消協程的函數，可以在協程內部或外部調用。
- **取消例外 (Cancellation Exception)**：協程取消時拋出的例外，可以用來傳遞取消原因。
- **合作式取消 (Cooperative Cancellation)**：協程取消的一種方式，協程必須主動檢查並響應取消請求。
- **Fibonacci 函數**：一種遞歸算法，用於計算 Fibonacci 數列。
- **遞歸 (Recursion)**：函數在內部調用自身的一種編程技術，常用於解決重複性問題。
- **withTimeout**：Kotlin 中的一個掛起函數，設置協程執行的最大時限，超過時限將自動取消協程。
- **超時 (Timeout)**：在特定時間內未完成的操作將被終止或取消的機制。
- **測試 (Testing)**：在開發過程中檢查程式碼是否正確執行的過程。
- **Logcat**：Android 中的日誌系統，用於查看應用程式的輸出訊息和錯誤。
- **重複 (Repeat)**：一種編程結構，用於多次執行相同的代碼區塊。
- **協程 (Coroutine)**：Kotlin 中的輕量級執行單位，可以在一個執行緒內運行多個協程。
- **掛起函數 (Suspend Function)**：Kotlin 中一種特殊的函數，只有在協程或其他掛起函數內才能執行。
- **延遲 (Delay)**：協程中的一個函數，用於暫停協程執行一段時間而不阻塞執行緒。
- **非同步執行 (Asynchronous Execution)**：在不阻塞主執行緒的情況下執行任務，允許多個任務同時進行。
- **順序執行 (Sequential Execution)**：按照順序逐個執行任務，前一個任務完成後再開始下一個任務。
- **Dispatchers.IO**：分派器的一種，適合用於執行 I/O 密集型操作，如網路請求、文件讀寫等。
- **measureTimeMillis**：Kotlin 的一個函數，用於測量代碼塊的執行時間，以毫秒為單位返回時間長度。
- **全域範圍 (Global Scope)**：Kotlin 中的一個協程範圍，協程在這個範圍中會與應用程式的生命週期一致。
- **Job**：協程啟動後返回的控制單位，可以用來監控和控制協程的執行狀態。
- **join**：協程中的一個掛起函數，用來等待協程完成執行。
- **async**：Kotlin 中的一個函數，用於啟動一個協程並返回一個 deferred 物件，可以用於獲取協程的計算結果。
- **deferred**：一種特殊的協程結果容器，用於保存異步計算的結果。
- **await**：協程中的一個掛起函數，用於等待一個 deferred 物件完成並返回結果。
- **launch**：Kotlin 中的一個函數，用於啟動一個協程，但不返回任何結果，通常用於執行需要並行但不需要返回值的任務。
- **併發 (Concurrency)**：在同一時間處理多個任務或操作的能力，允許多個協程或執行緒同時運行。
- **重複 (Repeat)**：一種編程結構，用於多次執行相同的代碼區塊。
- **網路請求 (Network Call)**：應用程式向伺服器發送請求並等待回應的過程。
- **I/O 密集型操作 (I/O Intensive Operation)**：涉及大量輸入輸出操作的任務，如網路請求和文件讀寫。
- **主執行緒 (Main Thread)**：應用程式的主要執行緒，負責處理 UI 更新和主要邏輯。
- **測試 (Testing)**：在開發過程中檢查程式碼是否正確執行的過程。
- **協程 (Coroutine)**：Kotlin 中的輕量級執行單位，可以在一個執行緒內運行多個協程。
- **Global Scope**：Kotlin 中的一個協程範圍，協程在這個範圍中會與應用程式的生命週期一致，直到應用程式被銷毀為止。
- **生命週期範圍 (Lifecycle Scope)**：一種協程範圍，與 Android 活動或片段的生命週期相關聯，當活動或片段被銷毀時，範圍內的協程也會被取消。
- **ViewModel Scope**：一種協程範圍，與 ViewModel 的生命週期相關聯，當 ViewModel 被清除時，範圍內的協程也會被取消。
- **掛起函數 (Suspend Function)**：Kotlin 中一種特殊的函數，只有在協程或其他掛起函數內才能執行。
- **延遲 (Delay)**：協程中的一個函數，用於暫停協程執行一段時間而不阻塞執行緒。
- **無限迴圈 (Infinite Loop)**：一種程式結構，會持續不斷地執行某些指令，直到程式明確終止它。
- **內存洩漏 (Memory Leak)**：程式中不再需要的資源未被正確釋放，導致內存被佔用，影響應用程式性能。
- **Garbage Collection (GC)**：Java 和 Kotlin 中的自動內存管理機制，用於釋放不再使用的物件所佔用的內存空間。
- **Logcat**：Android 中的日誌系統，用於查看應用程式的輸出訊息和錯誤。
- **片段 (Fragment)**：Android 中的一種 UI 組件，能夠在活動中重複使用，並且有自己獨立的生命週期。
- **啟動意圖 (Intent)**：Android 中用於啟動活動或服務的訊息對象，包含目標活動或服務的信息。
- **onDestroy**：Android 活動或片段生命週期方法之一，當活動或片段被銷毀時調用，用於進行資源釋放等清理操作。
- **主執行緒 (Main Thread)**：應用程式的主要執行緒，負責處理 UI 更新和主要邏輯。
- **結束 (Finish)**：Android 活動中用於結束當前活動並返回到上一活動的方法。
- **協程取消 (Coroutine Cancellation)**：當協程範圍或生命週期結束時，終止協程的執行。
- **長時間運行計算 (Long-running Calculation)**：需要較長時間執行的計算任務，可能會阻塞執行緒或影響應用程式性能。
- **依賴項 (Dependencies)**：在專案中必須包含的外部庫或模組，以便使用特定功能。
- **空值 (Nullable)**：表示一個變數可以為空 (null) 的特性，在 Kotlin 中使用 `?` 表示可為空的類型。
- **協程 (Coroutine)**：Kotlin 中的輕量級執行單位，可以在一個執行緒內運行多個協程。
- **Global Scope**：Kotlin 中的一個協程範圍，協程在這個範圍中會與應用程式的生命週期一致，直到應用程式被銷毀為止。
- **Firebase Firestore**：Firebase 的雲端 NoSQL 資料庫服務，用於儲存和同步應用程式資料。
- **回呼地獄 (Callback Hell)**：嵌套多層回呼函數導致程式碼難以維護的情況，通常發生於多個異步操作依賴於彼此的情況。
- **Lambda 函數**：一種匿名函數，可以被作為參數傳遞或在程式碼中定義，用於簡化回呼函數的使用。
- **KTX**：Kotlin 擴展庫 (Kotlin Extensions)，專為 Kotlin 語言設計的 API 優化，簡化了 Android 和 Firebase 的使用。
- **依賴項 (Dependencies)**：在專案中必須包含的外部庫或模組，以便使用特定功能。
- **掛起函數 (Suspend Function)**：Kotlin 中一種特殊的函數，只有在協程或其他掛起函數內才能執行。
- **等待 (Await)**：協程中的一個掛起函數，用於等待非同步任務的完成，並返回結果。
- **Task**：Firebase 操作的返回類型，表示一個非同步操作的結果。
- **toObject**：Firestore 提供的一個函數，用於將文件數據轉換為指定的 Kotlin 類型。
- **withContext**：Kotlin 中的一個函數，用於在協程內部切換上下文，讓協程在不同的執行環境中執行。
- **Logcat**：Android 中的日誌系統，用於查看應用程式的輸出訊息和錯誤。
- **生命週期範圍 (Lifecycle Scope)**：一種協程範圍，與 Android 活動或片段的生命週期相關聯，當活動或片段被銷毀時，範圍內的協程也會被取消。
- **Kotlin Data Class**：Kotlin 中一種特殊的類型，用於表示數據結構，簡化了 getter、setter 和其他常用方法的定義。
- **UI 線程 (UI Thread)**：負責處理 Android 應用程式界面更新的線程，任何界面操作都必須在此線程中執行。
- **非同步操作 (Asynchronous Operation)**：不會阻塞主線程的操作，允許在其他線程上執行任務。
- **Intent**：Android 中用於啟動活動或服務的消息對象，包含目標活動或服務的信息。
- **集合 (Collection)**：Firestore 中的數據結構，相當於數據庫中的表，包含多個文檔。
- **協程 (Coroutine)**：Kotlin 中的輕量級執行單位，可以在一個執行緒內運行多個協程。
- **Retrofit**：一個強大的 Android 和 Java HTTP 客戶端，用於網路請求和 RESTful API 通訊。
- **JSONPlaceholder**：一個免費的 REST API，用於測試和原型設計，提供虛擬的資料來測試應用程式。
- **`enqueue`**：Retrofit 的一個方法，用於非同步執行 HTTP 請求並透過回呼處理結果。
- **回呼 (Callback)**：一種常見的非同步編程模式，用於在操作完成後通知結果。
- **`await`**：Kotlin 協程中的一個掛起函數，用於等待非同步操作完成並返回結果。
- **Dispatchers.IO**：分派器的一種，適合用於執行 I/O 密集型操作，如網路請求、文件讀寫等。
- **`awaitResponse`**：Retrofit 和 Kotlin 協程一起使用時的一個方法，用於等待並返回完整的 HTTP 回應。
- **回應物件 (Response Object)**：包含 HTTP 回應資料的物件，包括狀態碼、標頭和回應主體。
- **掛起函數 (Suspend Function)**：Kotlin 中一種特殊的函數，只有在協程或其他掛起函數內才能執行。
- **API 介面 (API Interface)**：定義網路請求的接口，用於指定請求的路徑、方法和參數。
- **`Call` 物件**：Retrofit 中的一個類型，表示一次 HTTP 請求的結果，可以同步或非同步地執行。
- **非同步操作 (Asynchronous Operation)**：不會阻塞主線程的操作，允許在其他線程上執行任務。
- **`GlobalScope.launch`**：Kotlin 中的一個方法，用於啟動一個在 `GlobalScope` 範圍內運行的協程。
- **I/O 密集型操作 (I/O Intensive Operation)**：涉及大量輸入輸出操作的任務，如網路請求和文件讀寫。
- **HTTP 請求 (HTTP Request)**：用戶端向伺服器發送的請求，通常用於獲取資料或執行某些操作。
- **清單 (List)**：Kotlin 和 Java 中的一種資料結構，用於存儲有序的資料集合。
- **`Response.body()`**：用於提取 HTTP 回應的主體資料，一般為 JSON 格式的資料。
- **`isSuccessful`**：HTTP 回應的一個方法，用於檢查請求是否成功（狀態碼 200-299）。
- **API 請求 (API Call)**：應用程式通過 API 向伺服器發送請求，獲取資料或執行操作。
- **異步編程 (Asynchronous Programming)**：在不阻塞主執行緒的情況下執行任務，使多個任務可以同時進行。
- **協程範圍 (Coroutine Scope)**：協程的執行環境，定義了協程的生命週期及其取消行為。
- **`try-catch` 區塊**：用於捕獲和處理程式中可能拋出的例外情況。
- **例外傳播 (Exception Propagation)**：例外從子協程向父協程傳播，最終可能導致應用程式崩潰。
- **`CancellationException`**：協程被取消時拋出的例外，不會導致程式崩潰，但會傳播給相關協程。
- **掛起函數 (Suspend Function)**：Kotlin 中的特殊函數，必須在協程或其他掛起函數內執行。
- **`launch` 函數**：啟動一個新的協程，並返回 `Job` 物件，通常用於不需要返回值的任務。
- **`async` 函數**：啟動一個新的協程，並返回 `Deferred` 物件，用於需要返回值的任務。
- **`Job`**：協程的控制單元，用於管理協程的執行和取消。
- **`Deferred`**：`Job` 的子類，代表一個可延遲的值，可以通過 `await` 來獲取結果。
- **`await` 函數**：用於等待 `Deferred` 的結果並處理異步操作的例外。
- **`LifecycleScope`**：與 Android 活動或片段的生命週期相關聯的協程範圍。
- **協程樹 (Coroutine Tree)**：協程之間的層次結構，例外和取消會沿著這個層次結構傳播。
- **非同步例外處理 (Asynchronous Exception Handling)**：協程中的例外處理機制，與傳統同步程式不同。
- **父協程 (Parent Coroutine)**：包含其他協程的協程，負責管理其子協程的生命週期。
- **子協程 (Child Coroutine)**：由父協程啟動的協程，其生命週期受父協程管理。
- **協程取消 (Coroutine Cancellation)**：當協程範圍或生命週期結束時，終止協程的執行。
- **例外處理模式 (Exception Handling Pattern)**：協程中正確處理例外的最佳實踐和模式。
- **`supervisorScope`**：協程範圍的一種變體，允許子協程的例外不會影響其他協程。