# 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:保存快照狀態與版本資訊,用於衝突偵測