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.mzph.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;形成完整的大数据处理链路。下面我们从…