kotlin flow的兩種SharingStarted策略的區別

一 兩種 SharingStarted 策略的區別:

  1. SharingStarted.Eagerly:
  • 立即開始收集上游流,即使沒有下游訂閱者
  • 持續保持活躍狀態,直到 ViewModel 被清除
  • 優點:響應更快,數據始終保持最新
  • 缺點:消耗更多資源,因為始終在收集數據
  1. SharingStarted.WhileSubscribed(5000):
  • 僅在有下游訂閱者時才開始收集
  • 停止收集后等待 5000 毫秒才真正停止上游流
  • 優點:更節省資源
  • 缺點:首次訂閱時可能有短暫延遲

選擇建議:

// 適合使用 Eagerly 的場景:
// 1. 需要立即獲取和保持數據最新狀態
// 2. 數據更新頻繁且重要的場景
val fragranceChannel = FragranceRepository.observeSelectedChannel().stateIn(viewModelScope, SharingStarted.Eagerly, DEFAULT_CHANNEL)// 適合使用 WhileSubscribed 的場景:
// 1. 數據不需要實時更新
// 2. 想要節省資源的場景
val acStatus = ACStatusRepository.acSwitchStatus.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)

兩種策略說明

這是一個典型的Flow訂閱場景。解釋上下游關系:

  1. 上游(Upstream)
// 在 ViewModel 中
val fragranceChannelMaterialCardView = ToggleButtonState(state = FragranceRepository.observeSelectedChannel()  // 這是上游數據源.stateIn(viewModelScope, SharingStarted.Eagerly, DEFAULT_CHANNEL),...
)
  1. 下游(Downstream)
// 在 Fragment 中
viewModel.fragranceChannelMaterialCardView.state.collect { pos ->  // 這是下游訂閱者binding.fragranceSelectedChannel = pos
}

流程說明:

  1. FragranceRepository.observeSelectedChannel() 產生數據
  2. .stateIn() 將Flow轉換為StateFlow
  3. Fragment中的 .collect 訂閱這個StateFlow
  4. 當上游數據變化時,下游會收到通知并更新UI

這就像一個管道:

數據源(Repository) -> StateFlow(ViewModel) -> 訂閱者(Fragment)
[上游]              [中轉站]               [下游]

使用 SharingStarted.Eagerly 意味著即使沒有下游訂閱,上游也會一直產生數據
如果改用 WhileSubscribed,只有在Fragment 訂閱時才會開始收集數據

二 SharingStarted.Eagerly示例

SharingStarted.Eagerly 的收集機制:

class WeatherViewModel : ViewModel() {// 上游數據源 - 模擬溫度傳感器private val temperatureSource = flow {var temp = 20while(true) {emit(temp++)delay(1000)println("上游發射溫度: $temp") // 日志觀察發射}}// 使用 Eagerly 立即開始收集val temperature = temperatureSource.stateIn(scope = viewModelScope,started = SharingStarted.Eagerly, // 立即開始收集initialValue = 0)init {// 可以在這里觀察收集到的值viewModelScope.launch {temperature.collect { temp ->println("ViewModel 內部收到溫度: $temp")}}}
}// UI層使用
class WeatherFragment : Fragment() {override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)// 即使這里暫時還沒有收集,上游也在發射數據// 延遲5秒后再開始收集lifecycleScope.launch {delay(5000)viewModel.temperature.collect { temp ->binding.tempText.text = "$temp°C"}}}
}

關鍵點解釋:

  1. 立即收集的意義

    • 即使沒有下游訂閱者,StateFlow 也會保持最新值
    • 當下游開始訂閱時,可以立即獲得最新狀態
    • 適合需要持續監控或后臺處理的場景
  2. 收集過程

上游發射溫度: 20  (立即開始)
上游發射溫度: 21
上游發射溫度: 22
上游發射溫度: 23
上游發射溫度: 24
[5秒后 Fragment 開始收集]
Fragment收到溫度: 24 (立即獲得最新值)
上游發射溫度: 25
Fragment收到溫度: 25
  1. 適用場景
class LocationViewModel : ViewModel() {// 位置追蹤需要持續進行,即使UI暫時不可見val location = locationManager.locationUpdates().stateIn(scope = viewModelScope,started = SharingStarted.Eagerly,initialValue = defaultLocation)
}
  1. 與 WhileSubscribed 的對比
// WhileSubscribed - 有訂閱者才收集
val temperature1 = temperatureSource.stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(5000),initialValue = 0
)// Eagerly - 立即開始收集
val temperature2 = temperatureSource.stateIn(scope = viewModelScope,started = SharingStarted.Eagerly,initialValue = 0
)
  1. 使用建議
  • 如果數據源消耗資源較大,建議使用 WhileSubscribed
  • 如果需要后臺持續處理或保持最新狀態,使用 Eagerly
  • 大多數UI狀態場景,推薦使用 WhileSubscribed
class CarViewModel : ViewModel() {// 車輛狀態需要持續監控 - 使用 Eagerlyval carStatus = carMonitor.status.stateIn(scope = viewModelScope,started = SharingStarted.Eagerly,initialValue = CarStatus.Unknown)// UI展示數據 - 使用 WhileSubscribedval uiState = dataSource.stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(5000),initialValue = UiState())
}

所以,Eagerly 的立即收集主要用于:

  • 需要持續后臺處理的場景
  • 狀態不能中斷的監控場景
  • 需要立即響應的關鍵數據流

三 SharingStarted.WhileSubscribed(5000) 示例

下面通過一個具體示例來說明下游停止收集的情況:

class TemperatureViewModel : ViewModel() {// 上游數據源private val temperatureSource = flow {var temp = 20while(true) {emit(temp++)delay(1000) }}// 使用 WhileSubscribed(5000) 轉換為 StateFlowval temperature = temperatureSource.stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(5000),initialValue = 0)
}// Fragment/Activity 中使用
class TemperatureFragment : Fragment() {override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)// 開始收集 - 這時上游 flow 會開始發射數據viewLifecycleOwner.lifecycleScope.launch {viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {viewModel.temperature.collect { temp ->binding.tempText.text = "$temp°C"}}}}
}

以下情況會導致下游停止收集:

  1. Fragment 進入 STOPPED 狀態(如切到后臺):
// repeatOnLifecycle 會在 STOPPED 時自動取消收集
// 但會等待 5000ms 后才真正停止上游 flow
onStop() {// 此時下游停止收集,但上游繼續運行 5000ms
}
  1. 顯式取消協程:
val job = lifecycleScope.launch {viewModel.temperature.collect { }
}// 取消協程會停止收集
job.cancel() // 上游會在 5000ms 后停止
  1. Fragment/Activity 銷毀:
onDestroy() {// lifecycleScope 取消導致收集停止// 上游會在 5000ms 后停止
}

WhileSubscribed(5000) 的好處是:

  • 短時間內重新訂閱時(如快速切換頁面)無需重啟上游 flow
  • 避免頻繁啟停上游帶來的開銷
  • 5000ms 后才真正停止,可以平衡資源使用和響應性

所以它特別適合:

  • 需要共享的開銷較大的數據流
  • 頁面快速切換的場景
  • 需要緩存最新值的場景

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/906485.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/906485.shtml
英文地址,請注明出處:http://en.pswp.cn/news/906485.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Windows_RustRover Rust語言開發環境構建

Windows_RustRover Rust語言開發環境構建 一、Rust語言簡介(一)起源與發展(二)語言特點(三)應用場景(四)社區與生態 二、RustRover(一)主要功能(二…

XCOSnTh-fatfsShell

#include "XCOSnTh.h" #include "ff.h" #include "stdio.h" static char pwd[1024]"1:"; static char pwdCount2; FRESULT lsExe(char *path,int(*printf)(const char* format, ...)) {FRESULT res;DIR dir;FILINFO fno;// 打開根目錄…

篇章十 消息持久化(二)

目錄 1.消息持久化-創建MessageFileManger類 1.1 創建一個類 1.2 創建關于路徑的方法 1.3 定義內部類 1.4 實現消息統計文件讀寫 1.5 實現創建消息目錄和文件 1.6 實現刪除消息目錄和文件 1.7 實現消息序列化 1. 消息序列化的一些概念: 2. 方案選擇&#xf…

中間件-seata

分布式事務seata 角色組成角色指責AT模式TCC模式 角色組成 TC:事務協調者,維護全局和分支事務的狀態,驅動全局事務提交或回滾。TM:事務管理者,定義全局事務的范圍:開始全局事務、提交或回滾全局事務。RM&am…

python代碼繪制某只股票最近90天的K線圖、均線、量能圖

運行代碼,要求輸入股票代碼和名稱,其他參數可省略 import akshare as ak import matplotlib.pyplot as plt import pandas as pd import mplfinance as mpf import matplotlib.dates as mdates import numpy as np import os from datetime import date…

Xilinx 7Series\UltraScale 在線升級FLASH STARTUPE2和STARTUPE3使用

一、FPGA 在線升級 FPGA 在線升級FLASH時,一般是通過邏輯生成SPI接口操作FLASH,當然也可以通過其他SOC經FPGA操作FLASH,那么FPGA就要實現在啟動后對FLASH的控制。 對于7Series FPGA,只有CCLK是專用引腳,SPI接口均為普…

Azure 應用服務中的異常處理、日志記錄和通知:綜合指南

簡介 Azure 應用服務是基于云的應用程序,使開發人員能夠在云上構建、部署和管理應用程序。與任何應用程序一樣,制定適當的異常處理、日志記錄和通知實踐至關重要,以確保應用程序平穩運行,并快速識別和解決任何問題。在本篇博文中&…

Java 應用如何實現 HTTPS:加密數據傳輸的實用指南

Java 應用如何實現 HTTPS:加密數據傳輸的實用指南 在當今的互聯網環境中,數據安全至關重要,HTTPS 作為加密的數據傳輸協議,為 Java 應用提供了安全通信的保障。本文將深入探討 Java 應用如何實現 HTTPS,通過詳細代碼實…

域名與DNS詳解

域名與DNS詳解 一、核心概念 域名(Domain Name) 定義:人類可讀的網絡地址標識(如 www.google.com)作用:替代復雜IP地址(類似"手機通訊錄"功能) DNS(Domain …

c++20引入的三路比較操作符<=>

目錄 一、簡介 二、三向比較的返回類型 2.1 std::strong_ordering 2.2 std::weak_ordering 2.3 std::partial_ordering 三、對基礎類型的支持 四、自動生成的比較運算符函數 4.1 std::rel_ops的作用 4.2 使用<> 五、兼容他舊代碼 一、簡介 c20引入了三路比較操…

計算機網絡相關面試題

一、HTTP1.1和HTTP2的區別 HTTP/1&#xff08;主要指 HTTP/1.1&#xff09;和 HTTP/2 是 Web 協議發展中的兩個重要版本&#xff0c;二者在性能、協議機制和功能特性上有顯著差異。以下從多個維度對比分析&#xff0c;并結合具體案例說明&#xff1a; 一、連接與請求處理方式 1…

圖論算法精解(Java 實現):從基礎到高頻面試題

一、圖的基礎表示方法 1.1 鄰接矩陣&#xff08;Adjacency Matrix&#xff09; 鄰接矩陣是表示圖的一種直觀方式&#xff0c;它使用一個二維數組來存儲節點之間的連接關系。對于一個有 n 個節點的圖&#xff0c;鄰接矩陣是一個 nn 的矩陣&#xff0c;其中 matrix [i][j] 表示…

江科大TIM定時器hal庫實現

定時器相關hal庫函數 hal庫的定時器函數相比于標準庫&#xff0c;多了很多的中斷回調函數&#xff0c;同時對于定時器的初始化也改成使用句柄一次性順帶連帶DMA等功能一起初始化了 typedef struct {uint32_t Prescaler; /*定時器的預分頻值*/uint32_t CounterMode; …

CentOS 10:啟動telnet服務

參考&#xff0c; 鳥哥私房菜 - 第七章、網路安全與主機基本防護&#xff1a;限制埠口, 網路升級與 SELinux 7.3.3 埠口與服務的啟動/關閉及開機時狀態設定 我們知道系統的 Telnet 服務通常是以 super daemon 來控管的&#xff0c;請您啟動您系統的 telnet 試看看。 1 要啟動 …

Taro 安全區域

目錄 一、問題描述 二、問題解決 1、頂部劉海區 2、底部小黑條 一、問題描述 安全區域主要是為了避免劉海屏或底部欄遮擋&#xff0c;而造成的不良顯示效果。 本次將針對以下兩點進行考量&#xff1a; 1、頂部劉海屏區 2、蘋果X底部小黑條 二、問題解決 通過Taro.getS…

【Java微服務組件】分布式協調P1-數據共享中心簡單設計與實現

歡迎來到啾啾的博客&#x1f431;。 記錄學習點滴。分享工作思考和實用技巧&#xff0c;偶爾也分享一些雜談&#x1f4ac;。 歡迎評論交流&#xff0c;感謝您的閱讀&#x1f604;。 目錄 引言設計一個共享數據中心選擇數據模型鍵值對設計 數據可靠性設計持久化快照 &#xff08…

在SpringBoot項目中,使用單元測試@Test

1.引入依賴 <!--單元測試Test的依賴--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>3.2.1</version> </dependency> 2.在src/test/java目錄…

在Java中,將Object對象轉換為具體實體類對象

在Java中&#xff0c;將Object對象轉換為具體實體類對象可以通過以下幾種方法實現&#xff1a; 1?.使用instanceof關鍵字進行類型檢查和轉換?&#xff1a; 首先&#xff0c;使用instanceof關鍵字檢查Object對象是否為目標實體類的類型。 如果是&#xff0c;則進行強制類型…

JAVA學習-練習試用Java實現“音頻文件的讀取與寫入 :使用Java音頻庫處理音頻數據”

問題&#xff1a; java語言編輯&#xff0c;實現音頻文件的讀取與寫入 &#xff1a;使用Java音頻庫處理音頻數據。 解答思路&#xff1a; 在Java中處理音頻文件通常需要使用第三方庫&#xff0c;例如javax.sound.sampled包&#xff0c;它提供了處理音頻文件的基本功能。以下是一…

Flink架構概覽,Flink DataStream API 的使用,FlinkCDC的使用

一、Flink與其他組件的協同 Flink 是一個分布式、高性能、始終可用、準確一次&#xff08;Exactly-Once&#xff09;語義的流處理引擎&#xff0c;廣泛應用于大數據實時處理場景中。它與 Hadoop 生態系統中的組件可以深度集成&#xff0c;形成完整的大數據處理鏈路。下面我們從…