---
# System prepended metadata

title: '[Video Note] [Android] [Jetpack Compose] Philipp Lackner - Full Guide to Jetpack Compose Effect Handlers'
tags: [Jetpack Compose, Android, Video Note]

---

# Full Guide to Jetpack Compose Effect Handlers
### Side‑Effect 定義與問題

* 在 Compose 中，任何「逃離 Composable 函式範疇」的動作皆屬副作用，如存取全域變數或發送網路請求。
* 副作用若直接寫在 Composable 內，重組次數與時機不可控，會造成重複執行、狀態錯亂或性能浪費。
* 透過效果處理器（Effect Handlers）將副作用封裝，可明確控制生命週期並降低不可預期行為。若仍堅持將副作用留在 UI 層，即使理解理論也難避免隱性錯誤。

### LaunchedEffect

* 以 Composable 形式存在，會在組成時啟動 coroutine，可安全呼叫 suspend 函式。
* 接收 key 參數：key 變動時先取消現行 coroutine，再以新值重新啟動；傳入 `true` 可保證只執行一次。
* 適用場景：一次性收集 Flow、進行動畫、啟動背景作業等。
* 取消與重新啟動由 Compose 管理，不需手動追蹤；忽略 key 機制等於放棄針對重組的精準控制。

### rememberCoroutineScope

* 於 Composable 中產生與組成生命週期綁定的 CoroutineScope，離開組成時自動取消。
* 僅用於回呼（如 onClick、onTextChange）啟動 coroutine；若放在 Compose 主體區塊仍會造成副作用重組問題。
* 在成熟架構中多數協程可交由 ViewModel 的 viewModelScope 處理，直接依賴 UI Scope 反而增高耦合度，除非確有需要啟動短生命週期協程才使用。

### rememberUpdatedState

* 解決協程或 callback 捕獲過時參數的問題，保留狀態的最新引用而不觸發重新執行。
* 常見案例：Splash 畫面倒數、計時器、動畫等僅需執行一次卻可能接收新版 lambda 的情境。
* 將最新值包裝於 `rememberUpdatedState` 後傳入協程，可避免因 UI State 變動而延長或重啟作業；若忽視此作法，容易導致行為與預期不符。

### DisposableEffect

* 適合在 Composable 內註冊需要「清除」的監聽器或資源，例如 LifecycleObserver。
* 接收 key；當 key 變動時先執行 onDispose 清理，再重新建立資源。
* onDispose 於 Composable 移出組成時觸發，確保移除觀察者、關閉連線、釋放記憶體。
* 重組期間不會重複建立監聽器，避免資源洩漏與重複回呼。

### SideEffect

* 於每次成功重組後執行，用來同步非 Compose 控制的外部狀態。
* 無 key；只負責在重組完成後寫入或更新外部物件，如記錄分析事件或設定第三方 SDK 值。
* 不適合執行耗時作業或需要清理的行為，只做快速同步。

### produceState

* 在 Composable 內建立 coroutine 產生「隨時間變化」的 State。
* 以 initialValue 為起始，透過 coroutineScope 持續修改 value ，Compose 自動重新繪製。
* 可替代 Flow + collectAsState，將非同步結果直接轉成可觀察狀態。
* 與 Composable 生命週期綁定，離開組成時協程自動取消。

### derivedStateOf

* 建立「依賴多個 State 計算得來」且可能昂貴的衍生值，具備快取與自動更新。
* 當依賴的 State 改變時才重新計算；其餘讀取皆回傳快取結果，減少重複運算。
* 適用於複雜字串拼接、清單過濾、昂貴資料轉換等場景。
* 避免在每次 recomposition 時進行重計算，提升效能。

### snapshotFlow

* 將 Compose State 轉換為 Flow，當 State 改變時發射新值。
* 支援 Flow operators（debounce、map、distinctUntilChanged 等）以進行反應式處理。
* 常用於需要觀察 UI 狀態並與非 Compose 層（例如資料層、Analytics）互動的情況。
* Flow 的生命週期由 collect 方負責；搭配 LaunchedEffect 或 ViewModelScope 可安全收集。

# 音樂 App
## LaunchedEffect
在 `Composable` 內啟動一段與 UI 生命週期綁定的 `Coroutine`。只有依賴值改變時才會重新啟動，離開 Composition 時自動取消。

----
當使用者切換到不同播放清單 `playlistId` 時，使用

```kotlin
LaunchedEffect(playlistId) { loadPlaylistTracks(playlistId) }
```

確保僅在 ID 改變時才重新向伺服器抓取曲目，避免多餘請求；若使用者迅速返回上一清單，協程亦會被取消，節省流量與電量。

---

## rememberCoroutineScope
在 Composable 中記住一個可用於啟動長生命週期工作的 `CoroutineScope`；Scope 會隨組件離開 Composition 一併取消。

----
「下載專輯」按鈕需要背景下載多首歌曲並持續顯示進度：

```kotlin
val scope = rememberCoroutineScope()
Button(onClick = {
    scope.launch { downloadAlbum(albumId) }
}) { /* … */ }
```

可避免因連點造成多支協程同時下載；Component 消失時自動終止未完成下載，防止殘留任務。

---

## rememberUpdatedState
將最新的 value 封裝進 `State<T>`，讓仍在執行的協程或回呼存取到最新值，而不需重新重組。常用於 `LaunchedEffect` 內部或回呼引用。

----
播放進度通知使用者偏好語言：

```kotlin
val currentLocale by rememberUpdatedState(appLocale)
LaunchedEffect(mediaPlayer) {
    mediaPlayer.setOnProgressListener { pos ->
        toast(formatTime(pos, currentLocale)) // 始終拿到最新 locale
    }
}
```

避免語系變更後仍顯示舊語言時間格式。

---

## DisposableEffect
在進入 Composition 時執行初始化，並在離開時執行清理邏輯。適合與系統 listener、廣播或第三方 API 綁定／解除。

----
註冊並釋放耳機插拔廣播：

```kotlin
DisposableEffect(Unit) {
    val receiver = HeadsetBroadcast { paused -> if (paused) pause() }
    context.registerReceiver(receiver, intentFilter)
    onDispose { context.unregisterReceiver(receiver) }
}
```

確保畫面關閉或組件銷毀時不遺留 Receiver，避免記憶體滲漏。

---

## SideEffect
在每次成功重組後執行一次同步函式；不屬於協程，主要用於與外部物件保持一致（例如測量、日誌）。

----
將目前可見曲目 ID 同步到原生 Android Window 標題（供多工檢視顯示）：

```kotlin
SideEffect { activity.title = currentTrack.title }
```

重組時若 `currentTrack` 變化，Window 標題即時更新，而不需額外 listener。

---

## produceState

在 Composable 內以協程「產生」一個 `State<T>`，允許持續發射新值；生命週期同 Composition。

----
持續讀取播放器緩衝百分比並以 `State` 回推 UI：

```kotlin
val bufferPct by produceState(0f, mediaPlayer) {
    while (true) {
        value = mediaPlayer.bufferedPercentage
        delay(500)
    }
}
LinearProgressIndicator(progress = bufferPct / 100f)
```

玩家重啟或畫面離開時，協程自動取消，不會繼續輪詢。

---

## derivedStateOf
根據一組 `State` 來源，計算出另一個只在來源變動且計算結果改變時才重組的衍生狀態；避免昂貴計算重複執行。

----
根據播放清單曲目與當前排序模式計算顯示列表：

```kotlin
val sortedTracks by remember(sortMode, tracks) {
    derivedStateOf { sortTracks(tracks, sortMode) }
}
LazyColumn { items(sortedTracks) { TrackRow(it) } }
```

`tracks` 未改變時改動 UI 排序不會重新下載封面，提升效能。

---

## snapshotFlow
將連續的 Compose `State` 讀取封裝為 Cold `Flow`，每次 snapshot 變動時發出新值，用於橋接 Compose 與非 Compose 協程 API。

----
監聽音量滑桿並節流寫入偏好：

```kotlin
LaunchedEffect(Unit) {
    snapshotFlow { volumeSliderPosition }
        .debounce(300)
        .collect { setPreferredVolume(it) }
}
```

Compose Slider 變化頻繁，但只在使用者停止拖動 300 ms 後才寫入，避免 I/O 風暴。

---

> 這些 API 多用於處理副作用與狀態同步。若只在 Recomposition 裡做純運算，可考慮避免不必要的協程或 Effect，以減少複雜度與潛在資源浪費。

# 選擇題
1. 在 Jetpack Compose 中，何謂「副作用」（side effect）？
   A. 只在預覽模式下執行的程式碼
   B. 超出 Composable 函式範圍、影響外部狀態或資源的操作
   C. 造成版面配置錯亂的 UI 變動
   D. 任何耗費 CPU 的運算
   答案：B

2. 為何不應該直接在 Composable 函式主體內呼叫網路 API？
   A. Composable 無法存取網路
   B. 可能造成 UI thread 崩潰
   C. 每次重組（recomposition）都會再次執行呼叫，難以控制次數
   D. 會違反 Android 6.0 之後的權限規範
   答案：C

3. LaunchedEffect 能夠執行 suspend 函式的主要原因是什麼？
   A. 其內部隱含了一個 CoroutineScope
   B. 編譯器會把 suspend 函式改寫成同步
   C. Compose runtime 會自動暫停主執行緒
   D. LaunchedEffect 預設使用 ThreadPoolExecutor
   答案：A

4. 當傳入 LaunchedEffect 的 key 參數改變時，系統會如何處理？
   A. 忽略新值，維持舊協程
   B. 取消舊協程並重新啟動區塊
   C. 立即關閉畫面
   D. 只執行一次新的協程而不取消舊的
   答案：B

5. 若對 LaunchedEffect 傳入固定值 true 作為 key，代表該區塊會如何執行？
   A. 只在首度進入 Composition 時執行一次
   B. 每次重組皆執行
   C. 完全不會執行
   D. 只在畫面旋轉時執行
   答案：A

6. 在 ViewModel 內以 SharedFlow 傳遞事件到 UI，於 Composable 中使用 LaunchedEffect 收集的好處為？
   A. 可以在背景執行緒更新 UI
   B. 避免因重組而重複 collect，確保只收集一次
   C. 自動快取事件歷史
   D. 免去權限檢查流程
   答案：B

7. 若在 Composable 函式主體直接呼叫 collect()，最大風險是？
   A. collect 會被忽略
   B. Flow 會停止發射
   C. 每次重組都重新收集，導致記憶體與執行緒浪費
   D. 編譯不會通過
   答案：C

8. rememberCoroutineScope 所回傳的 CoroutineScope 具有什麼生命週期？
   A. 與整個應用程式存活時間相同
   B. 與當前 Composable 的 Composition 存活時間相同，離開時自動取消
   C. 與 Activity 相同
   D. 與 ViewModel 相同
   答案：B

9. 哪一個場合最適合使用 rememberCoroutineScope？
   A. 在 onClick 回呼中啟動短暫的協程
   B. 長時間的資料同步作業
   C. 循環播放音樂服務
   D. 伴隨應用程式整體生命週期的監控
   答案：A

10. 在按鈕 onClick 裡啟動協程時，以下何者為最佳寫法？
    A. GlobalScope.launch { … }
    B. rememberCoroutineScope().launch { … }
    C. Thread { … }.start()
    D. LaunchedEffect(key1 = Unit) { … }
    答案：B

11. 為何不建議在 Composable 外層大量使用 rememberCoroutineScope 啟動長任務？
    A. Compose 不支援協程
    B. 該 Scope 只活在 UI 層，長任務更適合 ViewModelScope
    C. Kotlinx.coroutines 不穩定
    D. 會強制使用主執行緒
    答案：B

12. rememberUpdatedState 的功能是？
    A. 固定某個狀態值不再更新
    B. 提供可變的 State，避免觸發重組
    C. 在 effect 區塊中取得最新的參數或 lambda 引用，而不重新啟動協程
    D. 建立具自動取消能力的 Channel
    答案：C

13. 在啟動畫面中，若 onTimeout Lambda 可能被替換，但不希望延長計時，應怎麼做？
    A. 將 onTimeout 直接放進 key
    B. 使用 rememberUpdatedState 包裝 onTimeout，並保持 key 固定
    C. 將計時邏輯搬到 Activity
    D. 改用 Handler.postDelayed
    答案：B

14. 使用 rememberUpdatedState 後，要在 LaunchedEffect 中取得最新 Lambda，應該？
    A. 直接呼叫原來的 lambda 變數
    B. 透過 updatedLambda.value()
    C. 使用 by 委託取得 updatedLambda，再呼叫它
    D. 不可能取得最新值
    答案：C

15. 若使用 Animatable 搭配 LaunchedEffect 實作數字動畫，應該把哪個值作為 key？
    A. Animatable 物件本身
    B. 當前計數器（counter）的 State 值
    C. rememberCoroutineScope
    D. viewModelScope
    答案：B

16. LaunchedEffect 區塊會在何種情況被取消？
    A. 當 key 發生變化
    B. 當螢幕熄滅
    C. 當其他 Composable 產生副作用
    D. 永遠不會被取消
    答案：A

17. 相較於 rememberCoroutineScope，viewModelScope 更適合長時間任務，原因為？
    A. viewModelScope 使用多執行緒池
    B. 其生命週期與 ViewModel 對應，較不易被 UI 銷毀
    C. viewModelScope 自動重試失敗任務
    D. Compose 強制要求
    答案：B

18. 若在 Composable 中直接 collect Flow，可能造成下列何者？
    A. Flow 只會收集一次
    B. 當前協程會阻塞主執行緒
    C. 每次重組皆重啟收集，導致重複或洩漏
    D. Flow 會自動轉為 StateFlow
    答案：C

19. 若想保證 LaunchedEffect 區塊只在入場時收集一次 Flow，key 應該如何設置？
    A. 不傳 key
    B. 傳入 MutableState 物件
    C. 傳入固定值 true 或 Unit
    D. 傳入當前時間戳
    答案：C

20. 在 Jetpack Compose 中，哪一種情況會觸發 recomposition？
    A. 按下手機電源鍵
    B. State 或 MutableState 的值改變
    C. 裝置連線到 Wi‑Fi
    D. 從背景回到前景
    答案：B

21. LaunchedEffect 本身是什麼？
    A. 一個不可重用的類別
    B. 一個 composable 函式，必須在 Composable 範圍內使用
    C. 一個 Android Service
    D. 一個 View 類別
    答案：B

22. 在幾種 effect handler 中，下列哪個被視為最常使用？
    A. DisposableEffect
    B. LaunchedEffect
    C. SideEffect
    D. ProduceState
    答案：B

23. 若要避免重組導致重複執行網路呼叫，應該使用哪個 effect handler 包裹？
    A. LaunchedEffect
    B. rememberUpdatedState
    C. SideEffect
    D. rememberCoroutineScope
    答案：A

24. 透過 rememberCoroutineScope 啟動的協程，在 Composable 離開 Composition 時會？
    A. 自動取消
    B. 持續運行到應用結束
    C. 轉移至 Service
    D. 暫停並在回來時繼續
    答案：A

25. LaunchedEffect 可以放在下列何處？
    A. 任何 Kotlin 檔案
    B. Service onCreate
    C. 只能置於 Composable 函式內
    D. BroadcastReceiver
    答案：C

26. 需要在 effect 區塊內取得最新 lambda 實例，但又不想重新創建協程，應該採用？
    A. key = lambda
    B. rememberUpdatedState
    C. GlobalScope.launch
    D. DisposableEffect
    答案：B

27. 若 LaunchedEffect 的 key 不含可能變動的 lambda，而 lambda 本身卻被替換，會導致？
    A. 協程自動更新 lambda
    B. 協程持續使用舊的 lambda，邏輯錯誤
    C. 編譯失敗
    D. lambda 被 GC 回收
    答案：B

28. 在 UI 回呼（如 onClick）中正確啟動 coroutine 的方式是哪一個？
    A. viewModelScope.launch
    B. rememberCoroutineScope().launch
    C. LaunchedEffect(Unit) { ... }
    D. runBlocking { ... }
    答案：B

29. 不當的副作用實作可能產生的後果包括？
    A. 記憶體洩漏與重複網路請求
    B. 編譯速度變慢
    C. APK 體積增大
    D. 減少程式碼行數
    答案：A

30. LaunchedEffect 與 rememberCoroutineScope 的主要差異為何？
    A. 前者屬於 Composable 區塊，後者回傳 Scope 供回呼使用
    B. 前者只能用於回呼，後者只能用於 Effect
    C. 兩者完全相同
    D. rememberCoroutineScope 會自動重試協程
    答案：A
1. 在 DisposableEffect 中，關於 onDispose 區塊的敘述何者正確？
   A. 必須與 rememberCoroutineScope 結合使用
   B. 僅在 key 為 null 時才需要實作
   C. 用於在 Composable 離開 Composition 時釋放資源
   D. 僅能呼叫非同步（suspend）函式
   答案：C

2. 當 DisposableEffect 的 key 改變時，Compose 的行為為何？
   A. 重新執行 effect 區塊，且先執行 onDispose
   B. 直接取代舊區塊，不呼叫 onDispose
   C. 什麼都不做
   D. 僅在下一次重組時才移除舊區塊
   答案：A

3. 下列哪個情境最適合使用 DisposableEffect？
   A. 每秒發射 UI 值
   B. 建立並移除 LifecycleObserver
   C. 快取複雜計算結果
   D. 監聽 State 改變並轉為 Flow
   答案：B

4. DisposableEffect 與 LaunchedEffect 的主要差異為何？
   A. LaunchedEffect 可定義 onDispose，DisposableEffect 不行
   B. DisposableEffect 支援清理機制，LaunchedEffect 不支援
   C. 兩者皆不可傳入 key
   D. LaunchedEffect 只能在 Activity 內使用
   答案：B

5. 若要在 ViewModel 層監聽 Lifecycle 而非 UI 層，應使用？
   A. LaunchedEffect
   B. DisposableEffect
   C. LifecycleObserver 於 ViewModelScope
   D. ProduceState
   答案：C

6. SideEffect 會於哪個時機被呼叫？
   A. 每次成功完成 recomposition 後
   B. 每個畫面旋轉事件發生時
   C. 組成剛開始時
   D. 只有當 key 改變時
   答案：A

7. SideEffect 的理想用途為？
   A. 快取大量資料
   B. 更新非 Compose 狀態或第三方 SDK 屬性
   C. 處理複雜動畫
   D. 監聽 Lifecycle
   答案：B

8. SideEffect 區塊是否允許執行 suspend 函式？
   A. 允許，因內部有 CoroutineScope
   B. 不允許，只能執行同步程式碼
   C. 允許，但僅限 IO coroutine
   D. 允許，需搭配 rememberCoroutineScope
   答案：B

9. 如果需要將 Compose 的 State 轉換成 Flow，以便套用 Flow 運算子，應使用？
   A. collectAsState
   B. produceState
   C. snapshotFlow
   D. derivedStateOf
   答案：C

10. snapshotFlow 與 collectAsState 的功能關係為？
    A. 兩者皆將 Flow 轉為 State
    B. snapshotFlow 將 State 轉為 Flow；collectAsState 將 Flow 轉為 State
    C. 兩者皆將 State 轉為 Flow
    D. snapshotFlow 僅支援 StateFlow
    答案：B

11. snapshotFlow 產生的 Flow 具有下列哪項特性？
    A. 永遠是熱流 (Hot)
    B. 僅在有收集者時監聽 State 變更
    C. 預設緩衝 100 個元素
    D. 會自動重播前 5 個值
    答案：B

12. 在 snapshotFlow 中使用 distinctUntilChanged() 的主要目的為？
    A. 避免重複發射相同值
    B. 自動切換到 IO 執行緒
    C. 加速重組流程
    D. 將冷流轉為熱流
    答案：A

13. produceState 的初始值指定方式為？
    A. 於呼叫端指定 initialValue 參數
    B. 在區塊內用 emit(0)
    C. 於 CoroutineScope.launch 裡設定
    D. 使用 remember 更新
    答案：A

14. produceState 與 LaunchedEffect 皆可啟動 Coroutine；produceState 額外提供？
    A. 清理機制
    B. 直接回傳可觀察的 State 物件
    C. 監聽 Lifecycle 事件
    D. 轉換 State 為 Flow
    答案：B

15. 若要每秒更新 UI 上的倒數計時器，最佳做法為？
    A. LaunchedEffect + State
    B. DisposableEffect
    C. produceState 回傳 State
    D. SideEffect
    答案：C

16. derivedStateOf 的核心作用是？
    A. 將 State 轉為 Flow
    B. 快取依賴其他 State 的計算結果
    C. 提供 Lifecycle 清理點
    D. 發射 State 至外部 SDK
    答案：B

17. 若 derivedStateOf 內部依賴的 State 未變動，但多個 Composable 讀取該值，Compose 會？
    A. 每次讀取皆重新計算
    B. 使用先前快取值，不重算
    C. 重新啟動整個重組
    D. 觸發 SideEffect
    答案：B

18. 在 derivedStateOf 區塊內執行昂貴計算可帶來何種效益？
    A. 每次讀取都避免計算
    B. 減少不必要的 recomposition 計算負擔
    C. 自動轉換為 Lazy 計算
    D. 讓 CoroutineScope 自動管理
    答案：B

19. 若 derivedStateOf 內僅讀取常數，而無任何可觀察 State，其計算會？
    A. 每次重組都執行
    B. 只執行一次並快取
    C. 從不執行
    D. 直到 onDispose 才執行
    答案：B

20. 若需要在 Composable 離開畫面時自動移除觀察者並取消協程，應選用？
    A. GlobalScope.launch
    B. DisposableEffect
    C. SideEffect
    D. snapshotFlow
    答案：B

21. 下面哪一個 effect handler 不支援傳入 key？
    A. DisposableEffect
    B. LaunchedEffect
    C. SideEffect
    D. produceState
    答案：C

22. DisposableEffect 的 onDispose 區塊內適合執行何種操作？
    A. 建立資料庫連線
    B. 停止位置更新並移除回呼
    C. 更新 UI 文本
    D. 執行複雜計算並快取
    答案：B

23. SideEffect 是否能保證在 recomposition 失敗時被呼叫？
    A. 能
    B. 無此保證
    C. 只有在 key 改變時才保證
    D. 取決於編譯器最佳化
    答案：B

24. 當 snapshotFlow 觀察的 State 連續快速變動時，Flow 會？
    A. 丟棄中間值、只保留最新
    B. 預設全部發射，除非進一步操作
    C. 自動壓縮成集合
    D. 停止發射
    答案：B

25. 若 snapshotFlow 內部引用的 State 被移除 Composition，Flow 會？
    A. 永遠持續發射
    B. 自動完成 (complete)
    C. 轉為熱流
    D. 崩潰並拋出例外
    答案：B

26. produceState 區塊若需要釋放資源，應如何處理？
    A. 直接呼叫 cancel()
    B. 在區塊結尾使用 awaitClose
    C. 透過 DisposableEffect 包裹並於 onDispose 清理
    D. 不可能清理
    答案：C

27. 何時應避免使用 SideEffect？
    A. 僅需一次性初始化
    B. 每次 recomposition 都必須同步外部物件
    C. 需要使用 suspend 函式、長時間作業
    D. 同步非 Compose 狀態
    答案：C

28. 對 derivedStateOf 使用 by 委託 (val foo by derivedStateOf { ... }) 的好處為？
    A. 自動快取並提供可讀屬性
    B. 強制執行在 IO 執行緒
    C. 隱藏 StateFlow 細節
    D. 能自動清理
    答案：A

29. DisposableEffect 的 key 採用 LifecycleOwner 的意義在於？
    A. 每次 Activity 旋轉都執行 onDispose/onCreate
    B. 保證 Observer 與正確的 Lifecycle 綁定
    C. 只要畫面亮起就會重設
    D. 避免產生多餘的 Snapshot
    答案：B

30. 如果必須同時利用 Flow 運算子與 Compose State，以下組合最佳為？
    A. collectAsState → snapshotFlow → SideEffect
    B. snapshotFlow → Flow 運算子 → collectAsState
    C. derivedStateOf → DisposableEffect → GlobalScope
    D. produceState → DisposableEffect → snapshotFlow
    答案：B


# Terminology
- side effect: 在Jetpack Compose中，副作用是指那些超出可組合函數範圍的操作。例如，在可組合函數中進行網絡請求，會導致該函數每次重組成時都重新執行該網絡請求，這是需要避免的。
- LaunchedEffect: 這是一種在可組合函數中執行異步操作的效果處理器。例如，用於啟動一個協程來執行延遲或網絡請求，並確保這些操作不會隨著重組成而多次執行。
- Composable function: Jetpack Compose中的可組合函數，是用於定義UI的基本單元，允許UI的聲明式構建和重組。
- recomposition: 當可組合函數中的狀態發生變化時，Jetpack Compose會重新執行該函數，以反映最新的狀態變化，這個過程稱為重組成。
- Coroutine scope: 協程作用域，用於管理協程的生命週期，確保協程在適當的時機被取消。
- suspend function: 一種可以在協程中調用的函數，允許在不阻塞線程的情況下執行異步操作。
- SharedFlow: 一種流，允許多個收集器在不遺失事件的情況下共享數據，用於從ViewModel向UI層傳遞事件。
- State: Compose中的狀態是用於保存和管理UI狀態的對象，當狀態改變時會觸發重組成。
- Animatable: 一個可動畫的數值對象，允許對數值進行動畫操作。
- effect handlers: 在Jetpack Compose中用於管理和執行副作用的工具，例如LaunchedEffect。
- ViewModel: Android架構組件之一，用於管理UI相關的數據，並保持它們在配置變更（如旋轉屏幕）期間持續存在。
- collect: 在Kotlin中，用於收集Flow流中的數據。
- Flow: 一種Kotlin協程的API，用於表示異步數據流，可以發射多個值。
- key: 在LaunchedEffect中使用的一個參數，用於控制效果的重啟行為，當key的值改變時，效果會重新啟動。
- compose state: Jetpack Compose中的狀態，用於描述UI的當前狀態，並在狀態變更時觸發重組成。
- lambda: 匿名函數，允許在簡短的上下文中定義行為。
- animation: 在Jetpack Compose中，用於在UI元素之間過渡的動畫效果。
- snackbar: 一種短暫顯示的消息條，用於向用戶提供反饋或提示。
- nav controller: 用於管理應用程序內導航的控制器，幫助在不同屏幕之間進行導航。
- rememberCoroutineScope: 在 Jetpack Compose 中，`rememberCoroutineScope` 是一個用於創建協程（Coroutine）作用域的函數。它允許在組件（composable）中創建協程，並在該組件生命週期結束時取消所有未完成的協程。這有助於避免在組件重新構建時發起多個重複的協程。
- 協程（Coroutine）: 協程是 Kotlin 中用於非同步編程的輕量級機制，類似於線程但更有效率。它允許在代碼中簡潔地處理非同步任務，如網絡請求或長時間運行的計算，而不會阻塞主線程。
- 作用域（Scope）: 在這裡指的是協程的執行範圍。使用 `rememberCoroutineScope` 創建的作用域會與組件的生命週期綁定，當組件不再可見或結束時，其內部的協程也會被取消。
- 回調函數（Callbacks）: 指的是例如 `onClick`、`onTextChanged` 等在用戶操作或界面事件觸發時調用的函數。在這些回調函數中使用 `rememberCoroutineScope` 是安全的，因為它們不與組件的重新構建相關聯，不會導致多次不必要的協程啟動。
- ViewModel: 在具有良好架構的應用程序中，通常建議使用 ViewModel 來處理業務邏輯和維護狀態。ViewModel 已經有自己的作用域，因此在大多數情況下，可以通過 ViewModel 的作用域來管理協程，而不必使用 `rememberCoroutineScope`。
- 啟動屏幕（splash screen）: 應用程序啟動時顯示的介面，通常用於顯示品牌標誌或應用程序名稱，提供短時間的歡迎畫面。
- Lambda 函數: 匿名函數或可以作為參數傳遞的函數。
- onTimeout: 在啟動屏幕顯示一段時間後觸發的函數，用於處理啟動屏幕隱藏或其他相關操作。
- key: 在 Compose 中用於區分和管理效果（effect）的唯一識別符。
- rememberUpdatedState: Compose 中的一個 hook 函數，用於在狀態更新時通知 Compose 重新計算。當你需要在保持效果不重新啟動的同時更新依賴的狀態時，特別有用。
- delegate: 在 Kotlin 中，委託（delegate）是一種模式，允許你將一些職責委託給另一個對象。在這裡，使用 `rememberUpdatedState` 作為一種委託方式來管理狀態。
- 生命周期事件觀察者（Lifecycle Event Observer）：用於在 Compose 中截取生命周期事件，例如當活動進入暫停狀態時進行相應操作，如打印日誌或其他處理。
- 啟動效果（launch effect）：在 Compose 中一種用於處理副作用的機制，可以通過指定鍵（key）來管理效果的生命週期，但無法用於需要清理的場景。
- 一次性效果（disposable effect）：與啟動效果類似，但是可以通過實現 `onDispose` 函數來清理資源，特別適用於需要在 Compose 組件結束時進行清理的場景，例如移除生命周期監聽器。
- 生命周期所有者（lifecycle owner）：通常指的是活動（Activity）或片段（Fragment），在 Android 中用於管理生命周期事件的對象。
- 添加觀察者（addObserver）：將生命周期事件觀察者添加到生命周期所有者中，以接收相應的生命周期事件通知。
- 移除觀察者（removeObserver）：從生命周期所有者中移除生命周期事件觀察者，避免資源泄漏或不必要的事件通知。
- 清理資源（cleanup）：在 Compose 組件不再需要某些資源時進行相應的清理工作，通常用於處理回調函數等需要額外管理的資源。
- 副作用（side effect）：在 Compose 中，一種特定的效果處理機制，用於在組件成功重構時執行指定的 Lambda 區塊。
- 非 Compose 狀態（non-Compose state）：指的是不符合 Compose 管理狀態的資料或狀態，例如從 API 或第三方庫中獲取的數據。
- 重構（recompose）：Compose 中組件重新計算和渲染的過程，通常由於相依的狀態或數據改變而觸發。
- Firebase 使用者 ID（Firebase user ID）：Firebase 提供的用於識別和管理用戶的唯一識別符。
- 分析（analytics）：在軟體開發中，指的是收集、處理和分析用戶行為和應用性能的過程。
- Android 團隊文檔（Android team documentation）：官方 Android 開發文檔，提供了關於使用 Android 框架和庫的指導和範例。
- Produce State：這是一個可組合函數，返回一個`produceState`函數。該函數的目的是生成隨時間變化的某種狀態，類似於Flow。
- Coroutine Scope：`produceState`函數提供了一個協程範疇，可以在其中調用掛起函數（如`delay`）。
- Flow：一種可以發射多個值的Kotlin流式處理，這些值隨時間變化。使用Flow可以實現連續發射數據的功能。
- Collect as State：這是一種從Flow收集數據並將其轉換為Compose中的狀態的方法。
- Initial Value：初始化值，這裡指的是Flow或狀態初始的數值。
- Derived State Of：這是一個可組合函數，用於創建派生狀態。它會在第一次訪問時計算並緩存結果，以後的訪問將使用緩存的值，除非依賴的狀態發生變化。
- Counter State：一個初始值為0的計數狀態，表示計數器的當前值。
- Counter Text：一個顯示計數器當前值的文本。每次計數器更新時，這段文本會重新計算並更新。
- Recomputed：重新計算，指的是每次計數器狀態更新時，與計數器相關的文本也會重新計算。
- Cache：緩存，指的是將計算結果存儲起來，後續訪問時使用緩存的結果而不是重新計算。
- Concatenation：串聯操作，指的是將字符串和整數組合在一起的操作。
- Complex Action：複雜操作，指的是需要依賴狀態並進行大量計算的操作。
- Notify Composables：通知可組合函數，當派生狀態的依賴狀態發生變化時，自動通知相關的可組合函數進行更新。
- Snapshot Flow：這是一個可組合函數，用於將Compose狀態轉換為Flow。當Compose狀態變化時，它會發出值。
- Collect as State：這是一個從Flow收集數據並將其轉換為Compose中的狀態的方法。
- Scaffold State：這是一個Compose中的狀態，通常包含UI佈局相關的狀態，例如SnackBarHostState。
- SnackBarHostState：這是一個Compose中的狀態，用於管理SnackBar的顯示狀態。
- Map：這裡指的是Flow的操作符，用於將一個值轉換為另一個值。
- Distinct Until Changed：這是一個Flow的操作符，用於僅在值發生變化時才觸發新的發射。
- Flow Operators：這些是用於處理Flow中數據的操作符，例如`map`、`filter`、`distinctUntilChanged`等。
- Reactive Programming：這是一種編程範式，強調數據流和變化傳播，通常使用Flow或RxJava等工具來實現。
* Side Effect：在可組合函式之外改變狀態或執行 I/O 的動作，可能於不受控時間重複觸發
* Composable Function：Jetpack Compose 中可宣告 UI 的函式，具備可重組能力
* Recomposition：框架偵測狀態變化後重新執行可組合函式以更新 UI 的過程
* Jetpack Compose：Android 宣告式 UI 工具包，以 Kotlin DSL 描述介面
* Effect Handler：Compose 提供的 API，用於安全地執行副作用並管理生命週期
* LaunchedEffect：以 key 為依據啟動及取消協程的 effect handler，常用於一次性或鍵值變動時的副作用
* rememberCoroutineScope：回傳與組合生命週期綁定的協程範圍，適合在回呼中啟動協程
* rememberUpdatedState：保存最新物件參考，在不重新啟動協程的情況下提供更新值
* Coroutine：Kotlin 的輕量級非同步執行單元，支援掛起與取消
* CoroutineScope：限定協程生命週期與錯誤傳播邊界的容器
* Suspend Function：可掛起與恢復的函式，允許在協程內非阻塞呼叫
* Flow：冷流資料串流類型，按需發射序列值
* SharedFlow：熱流，可多播事件給多個收集者
* State：Compose 的可觀察資料類型，變更時觸發重組
* MutableState：可變版 State，透過 setter 更新值
* Animatable：用於在協程中平滑插值數值的可動畫對象
* remember：在重組間保留物件，僅於首次組合時初始化
* Key：傳給 effect handler 的辨識值，用來決定是否重啟副作用
* Scaffold：提供一致佈局結構（如 AppBar、FAB）之組合容器
* SnackbarHostState：管理 Snackbar 顯示佇列的狀態物件
* NavController：Navigation Compose 的導覽控制器，負責畫面轉換
* ViewModel：儲存 UI 狀態並持有商業邏輯的生命週期感知類別
* viewModelScope：與 ViewModel 同生共死的協程範圍，適合執行背景任務
* launch：在指定 CoroutineScope 內啟動新的協程建構器
* collect：在協程中取得 Flow 發射的值並處理
* cancel：停止協程並觸發取消例外，防止資源浪費
* delay：非阻塞地暫停協程指定時間
* Callback：事件回呼函式，於使用者互動或流程完成時觸發
* onClick：元件被點擊時呼叫的 Lambda 回呼
* Lambda：Kotlin 的匿名函式表示法，可作為一級公民傳遞
* rememberSaveable：可跨組成和組件重建保存狀態的輔助函式
* Snapshot：Compose 內部不可變狀態快照，用於一致性檢查
* CompositionLocal：在組合層級注入與取得依賴的機制
* rememberInfiniteTransition：建立無窮動畫的狀態容器
* DisposableEffect：用於建立並清理需要明確釋放資源的副作用
* SideEffect（組合器）：於重組後一次性執行程式區段的 API
* produceState：外部資料源轉換為 Compose State 的協助函式
* derivedStateOf：根據其他 State 推導衍生 State，具備快取能力
* snapshotFlow：將 Snapshot 讀取轉換為 Flow，便於收集狀態變更
* rememberMutableStateListOf：建立可追蹤變動的可變 List 狀態
* EffectPhase：Compose 執行階段，分為應用、佈局、繪製等階段
* Composition：Compose 建構與維護 UI 節點樹的過程
* rememberLauncherForActivityResult：封裝 Activity 結果 API 的 compose 幫手
* coroutineContext：協程執行環境集合，包括 Job、Dispatcher 等元素
* CancellationException：協程被取消時拋出的例外，可攔截釋放資源
* 同步 (Blocking)：呼叫執行期間執行緒被占用，等待結果返回
* 非同步 (Non‑blocking)：呼叫立即返回，結果於未來時間點提供
* Cold Flow：直到收集者訂閱才開始發射資料的 Flow
* Hot Flow：無論是否有收集者都持續發射的流類型
* Backpressure：下游處理速度不足時的流量控制問題
* 宣告式 (Declarative)：以描述「呈現什麼」為主的 UI 範式
* 命令式 (Imperative)：以描述「如何做」的傳統 UI 更新方式
* DisposableEffect：在組合階段建立副作用並要求提供 onDispose 回收邏輯的效應處理器
* onDispose：DisposableEffect 內必實作區塊，於可組合項離開組合樹時執行清理程式碼
* LifecycleEventObserver：觀察 Activity/Fragment 生命週期轉換並回傳事件的介面
* LifecycleOwner：提供 Lifecycle 以供觀察者註冊的持有者（如 Activity 或 Fragment）
* Lifecycle.State：描述元件當前所處的生命週期狀態列舉，例如 RESUMED、PAUSED
* Composition Exit：可組合項從組合樹移除的階段，適合釋放資源
* rememberObserver：以 remember 儲存生命週期觀察者，避免重組時重複建立
* SideEffect（API）：於成功重組後僅執行一次區塊，用以同步非 Compose 狀態
* Atomic Snapshot：Compose 執行快照，確保狀態一致性與執行緒安全
* SnapshotAppliedCallback：快照套用完畢後觸發的回呼，可追蹤狀態提交
* produceState：以協程產生可隨時間變化的 State，並自動重新組合使用者
* initialValue 參數：produceState 的初始值，用作第一次組合前的回傳
* Coroutine Cancellation：透過 Job.cancel() 終止協程並釋放資源
* MutableStateFlow：熱流實作，具備高階別同步狀態能力並支援重播
* MutableSharedFlow：通用事件匯流，可設定緩衝與重播策略供多消費者使用
* distinctUntilChanged：Flow 運算子，過濾連續重複值避免多餘發射
* snapshotFlow 轉換：將可組合 State 封裝為 Flow，在狀態改變時發射
* FlowCollector：收集 Flow 發射元素的介面，執行處理邏輯
* BackpressureStrategy：控制生產速率超過消費速率時的處置方式
* derivedStateOf：根據其他 State 推導衍生值並快取，僅於依賴變動時重算
* ReadObserver：監聽 State 被讀取以便追蹤依賴關係的內部機制
* StructuralEqualityPolicy：Compose 預設比較策略，值內容相同即視為無變化
* ReferentialEqualityPolicy：使用參考比較避免深度比較提升效能
* Snapshot Invalidation：狀態改變導致快照失效，需要重新計算依賴
* SuspensionPoint：協程暫停等待非阻塞結果的位置
* rememberInfiniteTransition：管理無限動畫狀態並在重組間保留
* animate\*AsState：根據目標值平滑插值並回傳可組合的動畫 State
* key1、key2 參數：LaunchedEffect 等 API 的鍵值，用於判斷是否重啟副作用
* DisposableEffectResult：onDispose 區塊返回型別，代表清理操作執行結果
* NonRestartableComposable：標註可組合項不可在熱重新載入期間重啟的註解
* rememberSaveableStateHolder：在多重導航層級保存/恢復 State 的輔助類
* MapState：將 Map 包裝為可觀察 State，隨鍵值改變觸發重組
* produceLazyPagingItems：將 PagingData 轉為 Compose 列表專用狀態容器
* LaunchedEffect keySet：可傳入變動集合做為鍵值，集合內容改變即重啟協程
* SnapshotMutationPolicy：自訂狀態比較與變異合法性的策略介面
* runningRecomposeScopes：Compose Runtime 追蹤當前進行中的重組範圍集合
* DisposableScope：DisposableEffect 提供的作用域，允許存取 CoroutineScope
* CompositionLocalProvider：注入本地依賴至組合階層，供子組合使用
* rememberUpdatedState λ：保持最新 Lambda 參考以避免重啟協程
* AndroidViewBinding：Compose 內嵌傳統 View 並透過 ViewBinding 操作
* ScrollState：可組合捲動狀態，提供偏移與控制函式
* DerivedState Recalculation：依賴變動觸發 derivedStateOf 重新計算快取
* Ambient Recomposition：基於 CompositionLocal 依賴變動觸發的重組
* rememberCoroutineContext：取得與當前組合關聯的協程上下文
* snapshotFlow Buffering：可對 snapshotFlow 設定緩衝避免丟失高頻事件
* coroutineScope{} 建構器：在掛起函式內建立子作用域並等待其完成
* DisposableEffectScope：提供 addDisposeListener 等 API 增強清理能力的範圍
* registerForActivityResult：封裝系統 Activity 回傳結果的 API，可結合 rememberLauncherForActivityResult
* Compose Runtime：負責比對、排程與執行重組邏輯的核心
* StateSnapshotHandle：保存快照狀態與版本資訊，用於衝突偵測
