RainBowT
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    Github:[PhilippLackner-KotlinFlows](https://github.com/RainBowT0506/PhilippLackner-KotlinFlows) # [Flow Basics](https://www.youtube.com/watch?v=ZX8VsqNO_Ss) ## 引言與播放清單介紹 - 計劃製作一個深入探討 Kotlin Flow 的播放清單。 - 之前已有一些關於 StateFlow、SharedFlow 和 Flow 的影片,但缺少一個全面深入的系列。 - Flow 在 Android 開發中非常重要,幾乎每天都會用到,因此希望提供一個從頭到尾的完整學習資源。 - 這個播放清單將分為三個影片: - 第一個影片介紹 Flow 的基礎及使用方式。 - 第二個影片將深入探討 Flow Operators。 - 第三個影片將詳細講解 StateFlow 和 SharedFlow。 ## Flow 基本概念介紹 ![CleanShot 2024-08-14 at 13.58.23](https://hackmd.io/_uploads/r16YwTKcR.png) - Flow 是 Kotlin 中一種能夠在一段時間內發射多個值的協程。 - 傳統的協程或延遲函數只能返回單一值,而 Flow 能解決這個問題,適用於像倒數計時器這類需要持續發射值的情境。 - Flow 是反應式編程的一部分,允許我們隨時處理數據的變化。 - Flow 可以類比為組裝線,依次處理數據並最終輸出。 ## 簡單倒數計時器範例 - 創建一個 ViewModel 並定義倒數計時器的 Flow。 - 使用 Flow Builder 創建一個能夠每秒減少倒數計時的 Flow。 - 在 MainActivity 中顯示倒數計時的結果,並介紹如何在 Jetpack Compose 中使用 Flow 的狀態。 ## 如何收集 Flow 的數據 - 使用 `collect` 函數收集 Flow 的數據,每當 Flow 發射新值時,`collect` 函數中的代碼塊就會被觸發。 - `collectLatest` 的用法:與 `collect` 的區別在於,當新值進來時,`collectLatest` 會取消之前尚未完成的數據處理,只處理最新的數據。 ## 熱流與冷流 (Hot Flow vs. Cold Flow) - Flow 是一種冷流 (Cold Flow),只有當有收集者時,才會開始發射數據。 - 熱流 (Hot Flow) 則會在無論是否有收集者的情況下,持續發射數據。 # [Flow Operators](https://www.youtube.com/watch?v=sk3svS_fzZM) ## 引言與本影片介紹 - 這是 Kotlin Flow 播放清單的第二部分,將深入探討 Flow Operators。 - 將從簡單開始,逐步增加複雜性,到影片結尾將介紹較為複雜的概念。 - Flow Operators 是用來決定 Flow 中的每次發射值會發生什麼事。 ## Flow Operators 基本介紹 - Flow Operators 是用來處理和轉換 Flow 中的發射值。 - 在上個影片中,我們使用了簡單的倒數計時器來演示 Flow 的基本用法。 - Flow 不僅能收集數據,還能應用各種操作符來轉換數據。 ## 範例操作符:Filter - Filter 操作符可以過濾 Flow 的發射值,只保留符合條件的值。 - 例如,可以只保留偶數的發射值,其他的則丟棄。 ## 範例操作符:Map - Map 操作符可以將發射值轉換為新的值。 - 例如,可以將發射值平方後再傳遞給下一步處理。 ## 範例操作符:OnEach - OnEach 操作符用來對每個發射值進行操作,但不會改變它們。 - 可以用來記錄或打印發射值,也可以與其他操作符鏈結使用。 ## 使用 Terminal Flow Operators - Terminal Flow Operators 會終結 Flow,將所有發射值進行最終處理。 - 範例操作符:Count - 計算符合條件的發射值數量。 - 例如,計算所有偶數發射值的數量。 ## 範例操作符:Reduce - Reduce 操作符將所有發射值累積處理,最終返回一個值。 - 例如,累加所有發射值,得到它們的總和。 ## 範例操作符:Fold - Fold 與 Reduce 相似,但 Fold 可以提供一個初始值作為累積的起點。 - 例如,從 100 開始累加發射值,最終得到總和。 ## Flattening 基本概念 - 在 Flow 中,Flattening 不僅適用於列表,還適用於 Flow 本身,允許將多個 Flow 合併為一個單一的 Flow。 - 常見的 Flattening 操作符包括 `flatMapConcat`、`flatMapMerge` 和 `flatMapLatest`。 ## 範例操作符:flatMapConcat - `flatMapConcat` 會逐一處理每個 Flow 的發射值,按順序處理完一個 Flow 後才開始處理下一個。 - 例如,將兩個 Flow 的結果依次發射到單一 Flow 中。 ## 範例操作符:flatMapMerge - `flatMapMerge` 與 `flatMapConcat` 的區別在於,它不等待前一個 Flow 完成,而是同時處理多個 Flow 的發射值。 - 適合用於不需要依賴順序的情況,但在一般應用中較少使用。 ## 範例操作符:flatMapLatest - `flatMapLatest` 像 `collectLatest` 一樣,只保留最新的發射值,如果有新的發射值出現,則取消前一個處理並開始處理新的值。 - 適合處理頻繁更新的狀態,確保只關注最新的結果。 ## Buffer 與 Conflate 操作符 - `buffer` 會將 Flow 的發射和收集分離到不同的協程中,允許 Flow 繼續發射,即使收集器尚未處理完之前的值。 - `conflate` 會丟棄舊的未處理值,只保留最新的發射值,適合用於只關注最新狀態的場景。 - `collectLatest` 則直接忽略先前未完成的收集,立刻開始處理最新的發射值。 # [StateFlow & SharedFlow](https://www.youtube.com/watch?v=za-EEkqJLCQ) ## 介紹 - 這是這個系列課程的第三部分,將討論 `StateFlow` 和 `SharedFlow`,並比較它們與 `LiveData` 的不同。 - 影片中會展示如何使用這些工具,以及在實踐中何時使用它們。 ## StateFlow 簡介 - `StateFlow` 是用來保存狀態的,類似於 `LiveData`,但沒有生命週期感知能力。 - `StateFlow` 會通知所有的收集器(`collector`)當有新變化時,並只保存一個單一值。 - 它屬於「熱流」(Hot Flow),即使沒有收集器,仍會執行動作。 ## StateFlow 的使用 - `StateFlow` 通常用於 `ViewModel` 中保存 UI 狀態。 - 一般會在 `ViewModel` 中宣告一個私有的可變版本(`MutableStateFlow`),僅供 `ViewModel` 修改。 - 公有的不變版本則供 UI 訂閱和收集。 ## 宣告 StateFlow - 私有的可變版本通常命名為 `_stateFlow`,並以 `MutableStateFlow` 宣告,需提供初始值。 - 公有的不變版本使用 `asStateFlow` 函數從可變版本中獲取。 ## 實作範例:計數器 - 建立了一個簡單的計數器範例,用於展示 `StateFlow` 的功能。 - 當計數器增加時,`StateFlow` 的值會更新並通知 UI。 - `StateFlow` 的值在裝置旋轉後仍會保持,因此 UI 會顯示正確的狀態。 ## 在 Compose 中使用 StateFlow - 在 Compose 中,`StateFlow` 可以輕鬆與 UI 綁定,但在 Compose 中建議使用 Compose 的 `State`,而非 `StateFlow`。 - 若從其他庫獲得 `StateFlow`,可以使用 `collectAsState` 進行收集。 ## 在 XML 中使用 StateFlow - 在 XML 中觀察和收集 `StateFlow` 需要注意使用 `repeatOnLifecycle` 函數來處理生命週期。 - 傳統使用 `lifecycleScope.launch` 並不足夠,需要搭配 `repeatOnLifecycle` 才能確保在 UI 的適當時機收集 `StateFlow`。 ## 建立方便的擴展函數 - 創建了一個方便的擴展函數 `collectLatestLifecycleFlow` 來簡化 `StateFlow` 的收集。 - 這個函數可以減少代碼的縮排和重複,使代碼更簡潔。 - 使用這個函數後,只需要一個簡單的呼叫即可處理 `StateFlow` 的收集和 UI 更新。 ## 比較 StateFlow 與 LiveData - `StateFlow` 擁有更多的操作符,可以進行如 `map` 和 `flatMap` 等操作,比 `LiveData` 更加靈活。 - 使用 `StateFlow` 可以更好地利用協程,這在現代 Android 開發中是更推薦的方式。 ## 介紹 SharedFlow - 這部分將討論 `SharedFlow`,並比較它與 `StateFlow` 和一般的 `Flow` 的不同之處。 - `SharedFlow` 主要用來傳送一次性事件,例如顯示 Snackbar 或導航到另一個畫面。 ## SharedFlow 與 StateFlow 的比較 - `StateFlow` 是用來保存狀態的,例如螢幕旋轉後仍需保持的狀態。 - `SharedFlow` 用來處理一次性事件,這些事件在螢幕旋轉後不應該再次觸發,例如導航事件。 ## SharedFlow 的用途 - `SharedFlow` 可用於傳送例如顯示 Snackbar 或觸發導航的事件。 - 若事件需要被多個觀察者(`collector`)接收,使用 `SharedFlow` 是合適的選擇。 ## SharedFlow 的特性 - 與 `StateFlow` 一樣,`SharedFlow` 也是「熱流」(Hot Flow),即使沒有收集器,事件也不會被保留。 - 若在事件發送時沒有收集器,事件將會丟失,不會被保留在 `SharedFlow` 中。 ## 宣告 SharedFlow - 在 `ViewModel` 中宣告 `SharedFlow` 與 `StateFlow` 相似,不需要初始值。 - 可以使用 `emit` 函數從任何位置發送事件。 ## SharedFlow 的行為 - 當事件發送時,所有的收集器都會接收到該事件,且 `emit` 函數會暫停,直到所有收集器處理完成為止。 - 需要注意,如果事件在收集器初始化之前發送,事件將會丟失。 ## 設置 Replay Cache - 可以設置 `SharedFlow` 的 `replay` 參數來緩存事件,允許新加入的收集器接收到已發送的事件。 - `replay` 參數設置為 5,表示最多可以緩存 5 個事件供新收集器接收。 ## 使用 SharedFlow 的範例 - 宣告一個 `SharedFlow`,並發送一個事件後等待收集器處理該事件。 - 設置 `replay` 參數,使事件在發送後,即使沒有收集器,也能被後續的收集器接收到。 ## 在 Jetpack Compose 中使用 SharedFlow - 在 Jetpack Compose 中使用 `SharedFlow` 需要透過 `launchEffect`,這樣可以確保該區塊只會被啟動一次。 - 使用 `collect` 函數來收集 `SharedFlow` 中的事件並更新 UI。 ## 使用 SharedFlow 的注意事項 - 當在 XML 中使用 `SharedFlow` 時,避免使用 `collectLatest`,以確保所有事件都被正確處理。 - 可建立另一個版本的函數來處理 `SharedFlow` 的收集,確保事件不會被遺失。 # [Unit Testing Flows](https://www.youtube.com/watch?v=rk6aKkWqqcI) ## 介紹 - 這是 Kotlin Flows 教程的第四部分,主要介紹如何對 Flows 進行單元測試。 - 在大多數專案中,處理 Flows 是不可避免的,因此學習如何正確地進行單元測試非常重要。 ## 準備工作 ``` testImplementation("app.cash.turbine:turbine:0.7.0") testImplementation("com.google.truth:truth:1.1.3") testImplementation("org.jetbrains.kotlin:kotlinx-coroutines-test:1.5.1") ``` - 需要包含一些額外的依賴庫來進行單元測試: - **Turbine**:使得測試 Flows 更加簡單。 - **Truth**:Google 的一個庫,提供更好的測試斷言功能。 - **Coroutine Test**:提供一些協程測試功能,用於正確地測試協程。 ## 測試範例介紹 - 測試將針對一個倒數計時的 Flow,該 Flow 從 5 開始倒數,每秒減少一個值。 - 這類測試並不容易,因為涉及延遲和不同時間點的多次發射。 ## 建立測試文件 ![CleanShot 2024-08-15 at 21.54.35](https://hackmd.io/_uploads/HJmndFiqR.png) - 創建一個測試文件 `MainViewModelTest`,確保選擇 `JUnit 4` 並生成一個 `setup` 函數。 - 在 `setup` 中,每次測試前初始化 `ViewModel`,確保每個測試都是從乾淨的狀態開始。 ## 使用 Turbine 測試 Flow - `Turbine` 提供了一個 `test` 函數,可以直接用於測試 Flow。 - 在測試中使用 `awaitItem` 函數來等待 Flow 的發射,並將發射的值與期望值進行比較。 - 使用 `runBlocking` 來包裹測試,確保協程在測試結束前運行完畢。 ## 解決延遲問題 ``` Exception in thread "Test worker" java.lang.IllegalStateException: Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used ``` - 測試倒數計時的 Flow 時,每次執行測試都需要等待實際的延遲時間,這會使測試變得非常耗時。 - 解決方法是注入 `Coroutine Dispatcher`,這樣可以在測試中跳過延遲。 ## 注入 Coroutine Dispatcher - 創建一個 `DispatcherProvider` 介面,定義應用中需要的不同 `Dispatcher`。 - 創建一個具體的實現 `DefaultDispatchers`,在實際的 Android 專案中使用這個實現。 ## DispatcherProvider 的實現 - `DispatcherProvider` 介面包含主線程 (`Main`)、I/O 操作 (`IO`) 和默認 (`Default`) 的 Dispatcher。 - 在 `DefaultDispatchers` 類中為這些 Dispatcher 指定對應的 Coroutine Dispatcher。 ## 測試中的應用 - 在測試中,可以使用 `TestCoroutineDispatcher` 來代替默認的 Dispatcher,這樣可以跳過延遲。 - 測試時,使用這個自訂的 Dispatcher,保證測試快速且不依賴實際的時間流逝。 ## 將 Dispatcher 注入 ViewModel - 將 `DispatcherProvider` 介面注入到 ViewModel 中。 - 所有的協程必須使用這些注入的 Dispatcher,確保能在測試中使用自訂的 Dispatcher。 - 使用 `flowOn(dispatcher.main)` 來設置協程在主線程上運行。 ## 測試使用 Test Dispatcher - 在測試中,使用 `TestCoroutineDispatcher` 替代默認的 Dispatcher。 - 在測試類中創建一個 `TestDispatchers` 類來實現 `DispatcherProvider`,並使用 `TestCoroutineDispatcher`。 ## 測試倒數計時 Flow - 使用 `advanceTimeBy` 函數來跳過延遲,確保測試快速進行。 - 使用 `cancelAndConsumeRemainingEvents` 確保測試完成時處理所有剩餘事件。 ## 測試 Square Number Function - 測試 `SharedFlow` 時,需確保事件在收集器啟動後觸發。 - 使用 `launch` 創建一個協程,確保事件發送與收集器同步運行。 - 使用 `job.join()` 確保協程完成後再檢查結果。 ## 測試提示與最佳實踐 ``` Timed out waiting for 1000 ms kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1000 ms ``` - 測試時將事件發送與收集器分離,以避免未處理事件的問題。 - 測試結束時使用 `cancelAndConsumeRemainingEvents` 確保所有事件都被正確消耗。 - 建議注入 Dispatcher 作為測試中的最佳實踐,以便更靈活地控制協程行為。 # Clean Flow Transformations with Combine, Zip & Merge ## combine 運算符 ![CleanShot 2024-08-15 at 22.56.18](https://hackmd.io/_uploads/rylmDcjqR.png) - 用於合併多個 Flow 的值,當任一 Flow 有新值時就會觸發。 - 適合同時監聽多個數據來源,並在其中任何一個發生變化時更新狀態。 - 例子:將用戶資料和用戶帖子資料合併,並在任何一個發生變化時更新用戶的個人資料狀態。 ## 多個 Flow 的 combine ![CleanShot 2024-08-15 at 22.58.24](https://hackmd.io/_uploads/BJR5vqjcC.png) - 可以先合併部分 Flow,再與其他 Flow 進行合併,以處理更複雜的邏輯。 - 合併用戶認證狀態、用戶資料和帖子資料,並根據認證狀態決定是否更新個人資料。 - 使用 combine 可以減少多個收集器的重複操作。 ## zip 運算符 ![CleanShot 2024-08-15 at 22.59.45](https://hackmd.io/_uploads/Hyeld9o9C.png) ![CleanShot 2024-08-15 at 23.01.33](https://hackmd.io/_uploads/BJt8Oco9R.png) ![CleanShot 2024-08-15 at 23.01.58](https://hackmd.io/_uploads/B1Xudco9R.png) - 用於將多個 Flow 的值配對,當每個 Flow 都有新值時才會觸發。 - 與 combine 的不同在於 zip 會等待所有 Flow 都有新值時才進行配對。 - 適合需要同時配對多個 Flow 值的情況,例如將兩個不同速度發送數字的 Flow 進行配對。 ## merge 運算符 ![CleanShot 2024-08-15 at 23.02.40](https://hackmd.io/_uploads/rJ1s_cj9R.png) ![CleanShot 2024-08-15 at 23.02.54](https://hackmd.io/_uploads/Bkk2_cs9R.png) - 用於將多個 Flow 合併成一個 Flow,所有 Flow 的值會依次被發送。 - 不會等待其他 Flow,立即發送任一 Flow 的值。 - 適合將多個 Flow 的發送值合併成一個連續的 Flow,以便更簡單地進行處理。 # Terminology - **Kotlin Flow**:一種Kotlin的協程框架,允許在一段時間內發射多個值,適合處理異步數據流。 - **StateFlow**:一種特殊的Flow,用於表示和跟蹤應用狀態的變更。 - **SharedFlow**:另一種特殊的Flow,允許多個收集器共享同一數據流。 - **Coroutines**:Kotlin中的一種輕量級並發工具,用於執行異步操作。 - **Suspend Function**:Kotlin中的一種函數,可以暫停執行並稍後恢復,通常與協程一起使用。 - **Reactive Programming**:一種編程範式,關注於數據流的變更,並在數據變化時觸發相應的反應。 - **Flow Builder**:用於創建Flow的一個Lambda表達式,可以在其中定義如何生成和發射數據。 - **Flow Collector**:用於收集和處理Flow發射的數據,通常用於響應Flow的變化。 - **Emit**:Flow中用於發射新數據的函數,每當Flow中有新數據產生時會觸發。 - **Collect**:用於收集Flow發射的數據,每當有新數據時,Collect的代碼塊會被執行。 - **Collect Latest**:類似於Collect,但會在新的數據到來時取消當前處理的數據,只保留最新的數據。 - **Cold Flow**:一種僅在有收集者時才開始發射數據的Flow。 - **Hot Flow**:即使沒有收集者,也會持續發射數據的Flow。 - **Jetpack Compose**:Kotlin的一種UI框架,用於構建Android界面,與Flow結合使用時可以實現響應式UI更新。 - **ViewModel**:Android架構組件中的一部分,用於管理UI相關的數據和邏輯,並在配置更改時保存數據。 - **ViewModelScope**:ViewModel中的一個協程範圍,用於執行需要與ViewModel的生命週期相關聯的協程操作。 - **Flow Operators**:Kotlin Flow中的一系列函數,用於控制Flow發射數據的處理方式,如過濾、轉換或終止流。 - **Filter**:Flow操作符,用於過濾Flow中的發射數據,只有符合條件的數據才會被傳遞給收集器。 - **Map**:Flow操作符,用於將Flow中的每個發射數據轉換為另一個值,類似於對列表中的每個元素進行映射。 - **OnEach**:Flow操作符,不會改變Flow中的數據,而是在每個數據發射時執行某些動作,如打印數據,並允許繼續鏈接其他操作符。 - **Collect**:用於收集Flow中的數據,通常是終止Flow的操作符,當Flow中的每個數據發射時執行指定的動作。 - **Collect Latest**:類似於Collect,但在新數據到來時會取消當前正在處理的數據,僅處理最新數據,適合UI狀態的反應。 - **Terminal Flow Operators**:終止Flow的操作符,通常用於對Flow中的所有數據進行彙總或計算。 - **Count**:終止操作符,計算符合條件的Flow數據發射次數,返回數量。 - **Reduce**:終止操作符,將Flow中所有數據逐步累加或處理,返回最終累積結果。 - **Fold**:類似於Reduce,但可以設置初始值,並從該值開始進行累積或處理。 - **Launch In**:用於將Flow啟動並運行在指定的協程範圍內,如ViewModelScope。 - **Flattening**:將多個Flow合併為一個Flow,類似於將多層結構展平成單層結構。 - **FlatMap Concat**:按順序處理Flow中的每個發射數據,等待前一個Flow完成後再處理下一個Flow。 - **FlatMap Merge**:同時處理多個Flow,不等待前一個Flow完成,並行處理多個結果。 - **FlatMap Latest**:僅保留最新的發射數據,丟棄正在處理的數據以處理最新的數據。 - **Buffer**:允許Flow的發射與收集操作在不同的協程中並行運行,不等待收集完成便繼續發射新數據。 - **Conflate**:如果Flow在收集過程中發射了多個數據,僅保留最新的一個,丟棄之前的數據。 - **Collect Latest**:取消當前的收集操作,直接處理最新的發射數據,適合處理最新狀態的場景。 - 終端運算子:指在資料處理流程的最後一步,通常是將資料流的結果輸出或進行最終操作。 - 扁平化(Flattening):將巢狀結構(如巢狀清單)展平成單一結構的過程。 - Flow:Kotlin中用於處理非同步資料流的類型,可以發射多個值並進行處理。 - 延遲(delay):在非同步流程中插入等待時間,通常用於模擬耗時操作。 - FlatMap:一種操作符,用於將一個Flow的每個元素轉換成另一個Flow,並將這些Flow合併成單一Flow。 - FlatMapConcat:按順序串接多個Flow的操作符,在前一個Flow完成後再開始下一個Flow。 - FlatMapMerge:同時執行多個Flow的操作符,而不等待前一個Flow完成。 - FlatMapLatest:只保留最新Flow的結果,丟棄之前的Flow。 - 緩衝(Buffer):使資料流的發射與收集在不同的協程中執行,避免因收集端的延遲而阻塞資料流。 - 合併(Conflate):跳過收集端未來得及處理的資料,只保留最新的發射值。 - CollectLatest:只處理資料流的最新發射值,當新的發射值出現時,丟棄尚未完成處理的舊值。 - **StateFlow**:用於保持狀態的Flow,類似於LiveData,但不具備生命週期感知能力,適合用來儲存和管理應用中的UI狀態。即使沒有觀察者,它仍然會保持最新的值,因此屬於熱流(Hot Flow)。 - **SharedFlow**:一種可以發射和重播事件的Flow,允許多個觀察者同時訂閱。不同於StateFlow,它不儲存狀態值,而是處理事件流,適合用於事件傳遞,如用戶輸入或網絡響應。 - **LiveData**:Android中的一種可觀察數據持有類型,具備生命週期感知能力,可自動調整數據更新,以避免在UI不活躍時進行更新,從而防止內存洩漏。 - **冷流(Cold Flow)**:只有在被收集時才會啟動的Flow,如果沒有觀察者,它將不會進行任何操作,典型用於懶加載和資源節省。 - **熱流(Hot Flow)**:無論有無觀察者,Flow都會持續運作和發射數據,適合用於需要即時更新的狀態管理。 - **ViewModel**:Android架構組件的一部分,用於保存UI相關的數據,即使在設備旋轉等配置變更時,數據仍然能保持不變,避免UI重新創建後數據丟失。 - **MutableStateFlow**:StateFlow的可變版本,允許在ViewModel內部修改狀態值,而UI只能觀察到StateFlow的不可變版本。 - **value**:StateFlow中存儲的當前值,可以通過這個屬性來讀取或修改StateFlow中的數據,類似於LiveData中的`getValue`和`setValue`方法。 - **repeatOnLifecycle**:在Android的Kotlin協程中使用的一個功能,用於在特定的生命週期狀態下啟動和停止協程。這樣可以避免內存洩漏和資源浪費。當`repeatOnLifecycle`被調用時,它會在指定的生命週期狀態(如`Lifecycle.State.STARTED`)進入時啟動協程,並在生命週期狀態離開時自動取消協程。這使得在Activity或Fragment的特定生命週期中運行長時間操作變得更加安全和高效。 - **StateFlow**:一種用於保持狀態的熱流,適合在Kotlin中管理UI狀態,特別是在ViewModel中存儲並管理UI狀態,使得旋轉設備或其他配置變更時,狀態能夠保持。 - **SharedFlow**:一種可以多次發射和重播事件的熱流,允許多個觀察者訂閱,用於事件傳遞而非狀態管理。 - **LiveData**:Android中的可觀察數據持有類型,具備生命週期感知能力,適合在UI層使用,但缺少像Flow那樣的操作符。 - **LifecycleScope**:一個與生命週期綁定的協程作用域,允許在Android組件的生命週期內安全地啟動和停止協程。 - **repeatOnLifecycle**:用於在特定的生命週期狀態下啟動和停止協程的函數,確保協程在指定的生命週期狀態內安全運行,並在狀態變化時自動取消或重新啟動。 - **collectLatest**:Flow中的一種操作符,用於只處理最新的數據發射,如果新數據發射前一個發射尚未完成,則取消前一個發射並處理最新發射。 - **launchWhenStarted**:舊版本的簡便方法,用於在生命週期處於STARTED狀態時啟動協程,但現在建議使用repeatOnLifecycle來代替。 - **ComponentActivity**:一個基礎的Activity類別,為使用Jetpack組件和協程提供了必要的支持。 - **collectLatestLifecycleFlow**:一個自定義的擴展函數,用於簡化在Android組件中收集StateFlow的代碼,使其在生命週期內安全運行並處理最新數據。 - **SharedFlow**:用於發送一次性事件的熱流,適合處理如顯示Snackbar或導航等只需觸發一次的事件。不同於StateFlow,SharedFlow不會儲存狀態值,而是處理事件流。 - **Channels**:類似於SharedFlow,但主要用於單一觀察者情境下的事件發送。若有多個觀察者,則更適合使用SharedFlow。 - **emit**:在SharedFlow中發送事件的方法。當事件被發送時,如果沒有訂閱者,則事件會被丟棄,不會儲存在Flow中。 - **replay**:SharedFlow的參數,用於設定事件的緩存數量。設置`replay`後,SharedFlow會儲存指定數量的事件,以供新訂閱者接收這些緩存的事件。 - **launchWhenStarted**:以前的協程啟動方式,現在被建議使用`repeatOnLifecycle`來代替。 - **suspend function**:Kotlin中的一種函數類型,可以掛起當前協程並非同步地執行操作,如`emit`函數。 - **collect**:收集SharedFlow或其他Flow的事件,用於處理和響應這些事件。 - **replay cache**:SharedFlow中的一種緩存機制,允許新的觀察者在訂閱時接收之前緩存的事件。 - **launchEffect**:在Jetpack Compose中使用,用於在組合外部執行非同步操作,適合用於處理一次性事件,如SharedFlow的收集。 - **ViewModel**:用於儲存和管理UI相關數據的架構組件,能夠在設備旋轉等配置變更時保留數據。 - **Jetpack Compose**:一種基於Kotlin的現代化UI工具包,用於構建Android的聲明式界面,不同於XML佈局。 - **單元測試(Unit Testing)**:測試程式中最小單位的行為,以確保其功能符合預期。 - **Flow**:Kotlin中的資料流,可以發射多個值並處理非同步操作。 - **延遲(delay)**:在Flow中插入的等待時間,通常用來模擬耗時操作。 - **Turbine**:一個Kotlin庫,用於簡化Flow單元測試,提供方法來等待並驗證Flow的發射結果。 - **Truth**:Google提供的斷言庫,用於替代傳統JUnit斷言,提供更可讀和強大的測試斷言。 - **runBlocking**:一個阻塞式Kotlin協程運行器,允許在測試中以同步方式運行協程,確保協程完成後測試才結束。 - **DispatcherProvider**:用於在應用中注入協程調度器的抽象接口,使得在測試中可以輕鬆替換協程調度器。 - **TestCoroutineDispatcher**:專門用於測試的協程調度器,允許控制協程的時間進程,模擬即時結果並加快測試速度。 - **setup**:在每個測試案例之前運行的初始化方法,用於設置測試的初始狀態。 - **Main Dispatcher**:Kotlin協程的主線程調度器,通常用於UI相關的協程,在單元測試中通常會被替換以適應非UI環境。 - **協程(Coroutine)**:Kotlin中用於非同步和並發編程的輕量級線程,允許順序的非同步代碼。 - **emit**:在Flow中發射數據的方法,用於生成Flow中的新值。 - **collect**:從Flow中收集數據的方法,通常用於處理發射的值。 - **ViewModel**:Android架構中的組件,用於存儲和管理UI相關的數據,即使在設備旋轉等配置變更時,數據仍能保持。 - **Jetpack Compose**:基於Kotlin的現代化UI工具包,用於構建Android的聲明式界面。 - **SharedFlow**:一種可以多次發射和重播事件的熱流,適合處理如顯示Snackbar或導航等一次性事件。 - **StateFlow**:用於保持狀態的熱流,適合在ViewModel中管理UI狀態。 - **replay**:SharedFlow的參數,用於設定事件的緩存數量,允許新訂閱者接收之前緩存的事件。 - **冷流(Cold Flow)**:只有在被收集時才會啟動的Flow,如果沒有觀察者,它將不會進行任何操作。 - **熱流(Hot Flow)**:無論有無觀察者,Flow都會持續運作和發射數據,適合用於需要即時更新的狀態管理。 - **依賴注入(Dependency Injection)**:將依賴物件傳入某個類別的方法,而非在類別內部直接建立物件,用以提升測試的靈活性與可控性。 - **DispatcherProvider**:用於提供協程調度器的接口,允許在應用中注入不同的協程調度器,尤其在測試中替換調度器以控制協程行為。 - **TestCoroutineDispatcher**:專門用於測試的協程調度器,允許控制協程的時間進程,如跳過延遲或模擬即時結果。 - **Flow On**:Kotlin中用於更改Flow運行協程調度器的操作符,用於指定Flow應該在哪個調度器上執行。 - **Main Dispatcher**:Kotlin協程的主線程調度器,通常用於UI相關的協程調度,但在測試中會被替換以適應非UI環境。 - **ViewModelScope**:一個用於ViewModel中的協程範圍,確保協程在ViewModel清理時被取消。 - **Lateinit**:Kotlin中的延遲初始化屬性,用於宣告在初始化前不需要馬上賦值的屬性,通常用於依賴注入的場景。 - **Turbine Test**:Turbine庫中的一個方法,用於Flow單元測試,允許輕鬆等待和驗證Flow的發射項目。 - **TestCoroutineScope**:一個特殊的協程範圍,專門用於測試環境中,允許控制協程的啟動和運行時間。 - **awaitItem**:Turbine庫中的方法,用於等待Flow中的下一個發射項目,並將其返回以進行驗證。 - **advanceTimeBy**:TestCoroutineDispatcher的方法,用於在測試中快速跳過指定的時間,模擬時間流逝。 - **cancelAndConsumeRemainingEvents**:Turbine中的方法,用於取消測試並消耗剩餘的事件,以避免未消耗的事件引發錯誤。 - **runBlocking**:Kotlin協程中阻塞當前線程的函數,允許在單元測試中以同步方式運行協程。 - **launch**:Kotlin中的協程構建器,用於啟動新的協程,允許非同步運行代碼。 - **join**:協程中的方法,用於等待另一個協程完成,常用於測試中確保協程完成後再進行驗證。 - **SharedFlow**:一種可以多次發射和重播事件的熱流,適合處理如顯示Snackbar或導航等一次性事件。 - **assertThat**:Truth庫中的斷言方法,用於驗證測試結果是否符合預期。 - **runBlockingTest**:用於測試協程的運行器,允許控制協程的運行和時間流逝。 - **Job**:Kotlin協程中的工作單元,代表一個協程的生命周期,可用於控制和監控協程的執行狀態。 - **emit**:在Flow或SharedFlow中發射數據的方法,用於生成和發送新值。 - **flowOn**:Kotlin中用於更改Flow運行協程調度器的操作符,指定Flow在哪個調度器上執行。它允許將Flow的發射和收集操作分別在不同的協程調度器上執行,以實現更靈活的並發控制。通常用於在背景線程中處理Flow的數據流,然後在主線程中收集結果。 - **Flow**:Kotlin中的資料流,可以發射多個值並處理非同步操作。 - **StateFlow**:一種熱流,用於保持狀態,適合在ViewModel中管理UI狀態。 - **SharedFlow**:一種熱流,用於發送一次性事件,適合處理如顯示Snackbar或導航等事件。 - **MutableStateFlow**:StateFlow的可變版本,允許在ViewModel內部修改狀態值。 - **MutableSharedFlow**:SharedFlow的可變版本,允許發送新的事件。 - **collect**:從Flow中收集數據的方法,通常用於處理發射的值。 - **emit**:在Flow或SharedFlow中發射數據的方法,用於生成和發送新值。 - **combine**:Flow運算符,用於合併多個Flow的值,當任一Flow發生變化時觸發。 - **zip**:Flow運算符,用於將多個Flow的值配對組合,僅在所有Flow都有新的值時觸發。 - **merge**:Flow運算符,用於將多個Flow合併為一個Flow,按發射順序接收所有值。 - **FlowOn**:用於更改Flow運行協程調度器的操作符,指定Flow在哪個調度器上執行。 - **onEach**:用於對Flow的每次發射執行操作的運算符,類似於側效應。 - **map**:Flow運算符,用於轉換Flow中每個發射的值。 - **flatMapConcat**:Flow運算符,用於將每個值轉換為另一個Flow,並按順序串接這些Flow。 - **flatMapMerge**:Flow運算符,用於同時收集多個Flow的值,並發射它們的結果。 - **flatMapLatest**:Flow運算符,類似於flatMapConcat,但僅保留最新的Flow結果。 - **collectLatest**:Flow運算符,僅處理最新的數據發射,忽略之前未完成的發射。 - **buffer**:Flow運算符,用於在不同的協程中處理Flow的發射和收集,提升性能。 - **conflate**:Flow運算符,丟棄未處理的舊值,只保留最新的發射。 - **launchIn**:將Flow在指定的協程範圍內啟動並收集的方法。 - **ViewModel**:Android中的架構組件,用於保存和管理UI相關數據。 - **ViewModelScope**:一個用於ViewModel中的協程範圍,確保協程在ViewModel清理時被取消。 - **repeatOnLifecycle**:一個用於在指定生命週期狀態下啟動和停止協程的函數。 - **DispatcherProvider**:用於提供協程調度器的接口,允許在應用中注入不同的協程調度器。 - **Main Dispatcher**:Kotlin協程的主線程調度器,通常用於UI相關的協程調度。 - **TestCoroutineDispatcher**:專門用於測試的協程調度器,允許控制協程的時間進程。 - **runBlocking**:Kotlin中的阻塞式協程運行器,用於測試中以同步方式運行協程。 - **runBlockingTest**:專門用於測試協程的運行器,允許控制協程的時間流逝。 - **CoroutineScope**:協程的範圍,控制協程的生命周期和上下文。 - **Job**:Kotlin協程中的工作單元,表示協程的生命周期,可用於控制協程的執行。 - **launch**:Kotlin中的協程構建器,用於啟動新的協程。 - **async**:Kotlin中的協程構建器,用於啟動新的協程並返回其結果的Deferred對象。 - **Deferred**:Kotlin中的協程結果的延遲對象,允許協程異步返回結果。 - **suspend function**:Kotlin中的掛起函數,允許非同步運行代碼。 - **LiveData**:Android中的可觀察數據持有類型,具有生命週期感知能力。 - **Turbine**:一個用於簡化Flow單元測試的庫,提供方法來等待並驗證Flow的發射結果。 - **Truth**:Google提供的一個斷言庫,用於替代傳統JUnit斷言,提供更可讀的測試斷言。 - **Kotlin Coroutine**:Kotlin中的非同步和並發編程工具,用於簡化異步操作。 - **Channel**:Kotlin中的通信管道,用於在協程之間傳遞數據。 - **Cold Flow**:只有在被收集時才會啟動的Flow。 - **Hot Flow**:無論有無觀察者,Flow都會持續運作和發射數據。 - **State**:Jetpack Compose中的狀態容器,允許UI在狀態變化時重組。 - **MutableState**:Compose中的可變狀態容器,允許在UI中動態更新狀態。 - **LaunchedEffect**:Compose中的一個效應,用於在組合外部啟動協程。 - **remember**:Compose中的記憶工具,允許在重組過程中保留狀態或對象。 - **snapshotFlow**:Compose中的一個擴展函數,將可組合狀態轉換為Flow。 - **collectAsState**:將Flow轉換為Compose中可觀察的狀態,適合在UI中使用Flow。

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    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

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully