欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > kotlin flow的两种SharingStarted策略的区别

kotlin flow的两种SharingStarted策略的区别

2025/5/20 10:30:04 来源:https://blog.csdn.net/qq_26296197/article/details/148060073  浏览:    关键词: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 后才真正停止,可以平衡资源使用和响应性

所以它特别适合:

  • 需要共享的开销较大的数据流
  • 页面快速切换的场景
  • 需要缓存最新值的场景

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词