kotlin flow的两种SharingStarted策略的区别

news2025/5/22 19:40:53

一 两种 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 = 20
        while(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() {
    // 车辆状态需要持续监控 - 使用 Eagerly
    val carStatus = carMonitor.status.stateIn(
        scope = viewModelScope,
        started = SharingStarted.Eagerly,
        initialValue = CarStatus.Unknown
    )

    // UI展示数据 - 使用 WhileSubscribed
    val uiState = dataSource.stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000),
        initialValue = UiState()
    )
}

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

  • 需要持续后台处理的场景
  • 状态不能中断的监控场景
  • 需要立即响应的关键数据流

三 SharingStarted.WhileSubscribed(5000) 示例

下面通过一个具体示例来说明下游停止收集的情况:

class TemperatureViewModel : ViewModel() {
    // 上游数据源
    private val temperatureSource = flow {
        var temp = 20
        while(true) {
            emit(temp++)
            delay(1000) 
        }
    }

    // 使用 WhileSubscribed(5000) 转换为 StateFlow
    val 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.coloradmin.cn/o/2383344.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode-链表-合并两个有序链表

LeetCode-链表-合并两个有序链表 ✏️ 关于专栏:专栏用于记录 prepare for the coding test。 文章目录 LeetCode-链表-合并两个有序链表📝 合并两个有序链表🎯题目描述🔍 输入输出示例🧩题目提示🧪AC递归&…

sqli-labs靶场29-31关(http参数污染)

目录 前言 less29(单引号http参数污染) less30(双引号http参数污染) less31(双引号括号http参数污染) 前言 在JSP中,使用request.getParameter("id")获取请求参数时,如果存在多个同名参数&a…

JVM 垃圾回收机制深度解析(含图解)

JVM 垃圾回收机制深度解析(含图解) 一、垃圾回收整体流程 垃圾回收图解 #mermaid-svg-KPtxlwWntQx8TOj3 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-KPtxlwWntQx8TOj3 .error-icon{fill…

如何利用 Conda 安装 Pytorch 教程 ?

如何利用 Conda 安装 Pytorch 教程 ? 总共分为六步走: (1)第一步:验证conda 环境是否安装好? 1) conda -V2) conda --version(2)第二步:查看现有环境 conda env list…

uniapp vue 开发微信小程序 分包梳理经验总结

嗨,我是小路。今天主要和大家分享的主题是“uniapp vue 开发微信小程序 分包梳理经验总结”。 在使用 UniAppvue框架开发微信小程序时,当项目比较大的时候,经常需要分包加载。它有助于控制主包的大小,从而提升小程序的启…

什么是VR展示?VR展示的用途

随着科技的迅猛发展,我们步入一个全新的数字时代。在这个时代,虚拟现实(VR)技术崭露头角,逐步改变我们对世界的认知。全景展示厅作为VR技术与传统展览艺术的完美结合,以独特的全景视角,引领我们…

.NET外挂系列:4. harmony 中补丁参数的有趣玩法(上)

一:背景 1. 讲故事 前面几篇我们说完了 harmony 的几个注入点,这篇我们聚焦注入点可接收的几类参数的解读,非常有意思,在.NET高级调试 视角下也是非常重要的,到底是哪些参数,用一张表格整理如下&#xff…

Go语言中new与make的深度解析

在 Go 语言中,new 和 make 是两个用于内存分配的内置函数,但它们的作用和使用场景有显著区别。 理解它们的核心在于: new(T): 为类型 T 分配内存,并将其初始化为零值,然后返回一个指向该内存的指针 (*T)。make(T, ar…

3、ubantu系统 | 通过vscode远程安装并配置anaconda

1、vscode登录 登录后通过pwd可以发现目前位于wangqinag账号下,左侧为属于该账号的文件夹及文件。 通过cd ..可以回到上一级目录,通过ls可以查看当前目录下的文件夹及文件。 2、安装 2.1、下载anaconda 通过wget和curl下载未成功,使用手动…

【Unity】 HTFramework框架(六十五)ScrollList滚动数据列表

更新日期:2025年5月16日。 Github 仓库:https://github.com/SaiTingHu/HTFramework Gitee 仓库:https://gitee.com/SaiTingHu/HTFramework 索引 一、ScrollList滚动数据列表二、使用ScrollList1.快捷创建ScrollList2.ScrollList的属性3.自定义…

Swagger在java的运用

Swagger 是一个广泛使用的工具,用于设计、构建、记录和使用 RESTful Web 服务。它通过提供交互式的 API 文档、客户端 SDK 生成和 API 发现功能,极大地简化了 API 的开发和使用过程。以下是对 Swagger 的详细介绍,包括它的功能、使用场景、如…

代码随想录算法训练营 Day49 图论Ⅰ 深度优先与广度优先

图论 基础 图的概念 图的概念 概念清单有向图 (a)无向图 (b)有向/无向如图 a 所示每条边有指向如图 b 所示每条边没有箭头指向权值每条边的权值每条边的权值度-有几条边连到该节点 (eg V 2 V_2 V2​ 度为 3)入度/出度出度:从该节点出发的边个数入度:…

.NET外挂系列:1. harmony 基本原理和骨架分析

一:背景 1. 讲故事 为什么要开这么一个系列,是因为他可以对 .NET SDK 中的方法进行外挂,这种技术对解决程序的一些疑难杂症特别有用,在.NET高级调试 领域下大显神威,在我的训练营里也是花了一些篇幅来说这个&#xf…

HarmonyOS NEXT端云一体化工程目录结构

视频课程学习报名入口:HarmonyOS NEXT端云一体化开发 端云一体化开发工程由端开发工程(Application)和云开发工程(CloudProgram)两大核心模块构成。 1)端开发工程目录结构 端开发工程主要用于开发应用端侧的业务代码,通用云开发模板的端开发工程目录结构如下图所示: …

Ajax研究

简介 AJAX Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。 AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。 Ajax 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的Web应用…

学习 Android(十)Fragment的生命周期

简介 Android 的 Fragment 是一个具有自己生命周期的 可重用 UI 组件,能够在运行时灵活地添加、移除和替换,从而支持单 Activity 多界面、动态布局和响应式设计。掌握 Fragment 的生命周期有助于正确地在各个阶段执行初始化、资源绑定、状态保存与释放操…

RT Thread FinSH(msh)调度逻辑

文章目录 概要FinSH功能FinSH调度逻辑细节小结 概要 RT-Thread(Real-Time Thread)作为一款开源的嵌入式实时操作系统,在嵌入式设备领域得到了广泛应用。 该系统不仅具备强大的任务调度功能,还集成了 FinSH命令行系统&#xff0c…

单片机设计_四轴飞行器(STM32)

四轴飞行器(STM32) 想要更多项目私wo!!! 一、系统简介 四轴飞行器是一种通过四个旋翼产生的升力实现飞行的无人机,其核心控制原理基于欧拉角动力学模型。四轴飞行器通过改变四个电机的转速来实现六自由度控制(前后、左右、上下…

【Spring Boot】配置实战指南:Properties与YML的深度对比与最佳实践

目录 1.前言 2.正文 2.1配置文件的格式 2.2properties 2.2.1基础语法 2.2.2value读取配置文件 2.2.3缺点 2.3yml 2.3.1基础语法 2.3.2配置不同数据类型 2.3.3配置读取 2.3.4配置对象和集合 2.3.5优缺点 2.4综合练习:验证码案例 2.4.1分析需求 2.4.2…

算法优选系列(9.BFS 解决拓扑排序)

目录 拓扑排序简介: ​编辑 课程表(medium): 课程表II(medium): 火星词典(hard): 拓扑排序简介: 有向无环图(DAG图) 如上图每条边…