# 介紹  - 本次講座將介紹反應式編程概念如何應用於 Android 開發。 - 將學習如何使用 Kotlin 的 Flow 來建模資料流。 - 討論如何優化 Flow 以應對旋轉及應用程序進入後台等情況。 - 將會展示如何測試 Flow 確保其正常運行。 # Reactive  # 反應式編程概念      - 反應式編程強調觀察者會自動對被觀察對象的變化做出反應。 - 資料流應該保持單向流動,以降低錯誤並更易於管理。 - Flow 是 Kotlin 協程庫的一部分,用於處理數據流。 # Flow 的基本結構   - Flow 是一種 Kotlin 型別,用來表示資料流,類似於水流的概念。 - 資料源或資料庫通常是資料流的生產者,而 UI 是消費者。 - 在 Android 中,資料來源如 Room 可以通過 Flow 自動推送資料更新到 UI。 # 創建 Flow 的方式    - 大多數情況下,不需要手動創建 Flow,因為許多庫已與協程和 Flow 集成。 - Flow 建構器可以用來創建自定義的 Flow,尤其在需要定期查詢資料時。 - Flow 的執行是順序的,每次資料的更新都會通過 emit 函數推送到 Flow 中。   # Flow 的轉換與操作  - 使用中間操作符來轉換資料流,例如 map 操作符可將原始資料轉換為 UI 模型。   - 可以使用 filter 操作符來過濾資料,只保留特定條件的項目。  - 使用 catch 操作符來處理在資料流中出現的異常,例如捕捉並重新拋出異常或發出新值。     # 觀察 Flow  - 收集 Flow 通常發生在 UI 層,目的是將資料顯示在螢幕上。  - 使用終端操作符來開始監聽 Flow 的數據更新,例如 collect 操作符。  - Cold Flow 是指僅在被觀察時才開始發送數據的 Flow,每次調用 collect 都會創建新的 Flow。  # 在 Android 中收集 Flow 的最佳做法    - 必須考慮在應用進入後台時停止收集 Flow 以節省資源。 - 需要處理配置變更,如螢幕旋轉,確保 Flow 繼續正常運行。 - 可以使用 LiveData 或與生命周期相關的協程 API 來優化 Flow 的收集,避免在 UI 不顯示時浪費資源。 # Flow 轉換為 LiveData  - `asLiveData` 操作符可以將 Flow 轉換為 LiveData,這樣資料只會在 UI 可見時被觀察。 - 在 ViewModel 中進行這樣的轉換,然後在 UI 中以平常的方式消費 LiveData。 - 雖然這樣做很方便,但引入了額外的技術(LiveData),不一定是必要的。 # 使用 repeatOnLifecycle 收集 Flow     - `repeatOnLifecycle` 是收集 UI 層 Flow 的推薦方式,這是一個 suspend 函數,接受生命週期狀態作為參數。 - 當生命週期達到指定狀態時,自動啟動協程,當生命週期低於該狀態時,協程自動取消。 - 由於 `repeatOnLifecycle` 是 suspend 函數,它需要在協程中調用。 - 最佳實踐是在生命週期初始化時(例如 `onCreate`)調用此函數。 - `repeatOnLifecycle` 自動處理 UI 生命週期中的狀態變化,而不需要額外的代碼。 # Flow with Lifecycle API  - 如果只需要收集單個 Flow,可以使用 `flowWithLifecycle` 操作符來代替 `repeatOnLifecycle`。 - 這個 API 在生命週期進入和退出目標狀態時,會自動發送項目並取消底層的生產者。 # 不推薦的 Flow 收集方式     - 直接從 `lifecycleScope.launch` 收集 Flow 不是最安全的方法,因為即使應用在後台,UI 也會繼續更新。 - 這樣做可能導致資源浪費或危險情況,例如在應用後台顯示對話框可能導致應用崩潰。 # 旋轉與配置變更下的 Flow 收集   - 旋轉裝置或發生配置變更時,活動可能會重新啟動,但 ViewModel 仍然存在,因此不能簡單地暴露任何 Flow。 - `StateFlow` 是專門為此設計的,類似於水箱的概念,即使沒有收集者,它也會保留數據。    # 使用 StateFlow    - 可以將任何 Flow 轉換為 `StateFlow`,以便將最新值存儲並安全地用於 ViewModel 中。 - 使用 `stateIn` 操作符將 Flow 轉換為 `StateFlow`,其中包括初始值、協程範圍以及開始策略。 - 在旋轉場景中,`StateFlow` 保持活躍,確保體驗流暢,而在導航到主畫面時,則會暫停。    # 測試 Flow 的技巧        - 測試 Flow 可能會很複雜,但可以使用一些技巧。 - 在測試中,可以替換依賴為假設生產者,並設置所需的測試情景。 - 可以使用 Flow 的 `first` 方法或其他操作符來收集並驗證資料流。 # Resource  # Terminology - **Reactive Programming**:一種編程範式,透過資料流與變更傳播來建立非同步系統,觀察者會自動對所觀察對象的變化做出反應。 - **Flow**:Kotlin 協程庫中的一種資料流類型,用於建模數據流,可以是任何類型的資料,如使用者數據或 UI 狀態。 - **Producer**:在 Flow 中,負責將資料發送到流中的單元,通常是應用程式的數據來源或資料庫。 - **Consumer**:接收來自 Flow 的數據並使用這些數據的單元,通常是 UI 層,用來顯示資料。 - **Flow Builder**:一個用來創建 Flow 的建構器,可以在其中調用暫停函數,並使用 `emit` 函數將資料加入 Flow。 - **Suspend Function**:一種 Kotlin 中的特殊函數,允許協程在不阻塞主線程的情況下暫停執行。 - **Intermediate Operators**:用於轉換 Flow 中數據的運算符,例如 `map` 和 `filter`,它們創建新的 Flow 並將轉換後的數據發送到下一層。 - **Map Operator**:一種中介運算符,用於將 Flow 中的數據轉換為不同類型或格式的數據。 - **Filter Operator**:一種中介運算符,用於篩選 Flow 中的數據,僅保留滿足條件的數據。 - **Catch Operator**:用於處理 Flow 中異常的運算符,可以捕捉上游的異常,並根據需要重新拋出或發送新值。 - **Terminal Operator**:終端運算符,用於收集 Flow 中的數據,例如 `collect`,是啟動 Flow 的關鍵操作。 - **Cold Flow**:一種在被觀察時才會產生數據的 Flow,只有當終端運算符(如 `collect`)被調用時才會啟動。 - **Repeat on Lifecycle**:一種與 UI 生命週期相關的協程 API,用於在指定的生命週期階段重複執行特定的任務,避免在 UI 不可見時浪費資源。 - **Flow with Lifecycle**:一種協程 API,與 UI 生命週期掛鉤,使 Flow 的收集只在 UI 可見時進行,避免不必要的資源消耗。 - **LiveData**:Android 的一種數據持有者類型,能夠觀察並回應 UI 生命週期的變化。`asLiveData` 是 Flow 的一種操作符,用於將 Flow 轉換為 LiveData,這樣可以只在 UI 可見時觀察資料。 - **Repeat on Lifecycle**:一種協程 API,允許在指定的生命週期狀態下重複執行某些操作,並在生命週期低於該狀態時取消協程。這是 Android 中收集 Flow 的推薦方式。 - **LifecycleScope**:一種與 Android UI 生命週期相關的協程範疇,用於啟動協程並確保它們在 UI 元件的生命週期內適當地啟動和取消。 - **Flow with Lifecycle**:一種協程操作符,與 UI 生命週期掛鉤,當生命週期進入或離開目標狀態時,該操作符會自動開始或取消 Flow 的收集。 - **StateFlow**:Kotlin 協程庫中的一種流類型,用來持有狀態數據。它可以在沒有收集器的情況下保留最後的數據值,並允許多個收集器安全地收集數據,適合用於 ViewModel 中。 - **ViewModelScope**:一種協程範疇,與 ViewModel 的生命週期相關聯,確保協程在 ViewModel 被清除時取消。 - **WhileSubscribed**:一種在 StateFlow 中使用的策略,用於延遲上游 Flow 的取消,以應對短暫的 UI 暫停(如旋轉屏幕)而不影響用戶體驗。 - **SharedFlow**:Kotlin 協程庫中一種熱流類型,允許多個收集器接收相同的流數據,且不需要每個收集器都從頭開始接收數據。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up