kotlin中 热流 vs 冷流 的本质区别
🔥 冷流(Cold Flow) vs 热流(Hot Flow)区别
特性 | 冷流(Cold Flow) | 热流(Hot Flow) |
---|---|---|
数据生产时机 | 每次 collect 才开始执行 | 启动时就开始生产、始终运行 |
生命周期 | 与 collect 者强相关 | 与 Flow 创建源(如 StateFlow )有关 |
是否共享 | 否,每次 collect 独立 | 是,多个 collect 共享同一数据源 |
是否立即发射 | 否,除非指定操作(如 .onStart {} ) | 是,StateFlow 会立即发射当前值 |
常见类型 | flow {} / Retrofit 返回的 Flow | StateFlow , SharedFlow |
✅ StateFlow
属于热流,并且是立即发射的
写的:
private val _fanLevels = mutableMapOf<SeatPosition, MutableStateFlow<Int>>()fun observeFanLevel(position: SeatPosition): StateFlow<Int> = _fanLevels.getValue(position)
这表示:
- 每个
SeatPosition
都有一个MutableStateFlow<Int>
; - 一旦初始化,该
StateFlow
就始终存在、始终发射最新值; - 调用
observeFanLevel(position)
会立刻发射当前值(即first()
立即返回); - 属于 热流、可安全反复监听;
✅ ViewModel 中调用 first()
是安全的吗?
是的,如果 observeFanLevel(position)
返回的是 StateFlow
,则:
val current = levelFlow.first() // 这是“立即获取当前值”,是同步挂起行为
在这种情况下:
first()
会立即得到值(因为 StateFlow 始终有值);- 不会无限挂起;
- ✅ 所以 当前的用法在 ViewModel 中是安全的,不会存在性能/延迟问题。
⚠️ 注意两个潜在坑点
❶ 若未来换成 SharedFlow,first()
会挂起!
SharedFlow
不保证有初始值,因此:
val flow = MutableSharedFlow<Int>()
val current = flow.first() // ❌ 会挂起,直到有值发射!
🔧 若用 SharedFlow
,应使用:
flow.firstOrNull() ?: 0 // 或者使用 timeout 保护
❷ 若 map 中 StateFlow
没有预先创建,getValue() 会抛异常
现在:
fun observeFanLevel(position: SeatPosition): StateFlow<Int> = _fanLevels.getValue(position)
这要求 _fanLevels[position]
必须事先创建,否则会抛出 NoSuchElementException
。
✅ 如果在初始化阶段或通过 putIfAbsent
保证了 MutableStateFlow
已初始化,那没问题。
⚠️ 否则建议使用更安全方式:
fun observeFanLevel(position: SeatPosition): StateFlow<Int> {return _fanLevels.getOrPut(position) { MutableStateFlow(0) }
}
✅ 总结问题
问题 | 回答 |
---|---|
这是热流吗? | ✅ 是,StateFlow 是热流 |
会立即发射吗? | ✅ 会,StateFlow.value 本身就是当前值,first() 立即得到 |
在 ViewModel 中调用 first() 有问题吗? | ✅ 没问题,因为 StateFlow 不会挂起 |
observeXXX(position) 取不到会崩吗? | ⚠️ 如果 map 中没初始化该 position,会崩溃(建议改为 getOrPut) |
二 stateflow和shareflow区别
🧠 1. StateFlow vs SharedFlow 的本质区别
特性 | StateFlow | SharedFlow |
---|---|---|
是否有“当前值” | ✅ 有(类似 LiveData) | ❌ 没有(除非配置 replay > 0 ) |
初始值 | 必须有一个初始值 | 可选,无需初始值 |
最新值存储 | ✅ 始终保存最新值 | ❌ 默认不保存值 |
多收集器行为 | 所有收集器都会接收到最新的值 | 所有收集器会收到事件(是否丢失依赖 replay/buffer) |
生命周期感知 | ❌(不感知生命周期) | ❌ |
典型场景 | 状态展示,如 UI 状态 | 事件传递,如 Toast、导航、一次性命令等 |
✅ 举例说明:
📌 StateFlow
适合:UI 的状态数据绑定
val uiState = MutableStateFlow(UiState.Loading)fun loadData() {viewModelScope.launch {val result = repository.fetch()uiState.value = UiState.Success(result)}
}
优点:
- 始终保存最新状态
- 配合 DataBinding 或 Jetpack Compose 非常适合 UI 渲染
📌 SharedFlow
适合:一次性事件(如 Toast、导航)
private val _eventFlow = MutableSharedFlow<UiEvent>()
val eventFlow = _eventFlow.asSharedFlow()fun onLoginClick() {viewModelScope.launch {if (loginSuccess) {_eventFlow.emit(UiEvent.NavigateToHome)} else {_eventFlow.emit(UiEvent.ShowToast("登录失败"))}}
}
优点:
- 不保留值,每次 emit 是一次“广播”
- 多个收集器可以都收到该事件
- 配合
LaunchedEffect
、collect
适合 Compose 或传统监听回调
🚦 常见使用建议和实践
✅ StateFlow 的典型使用(MVVM 架构):
- ViewModel 层:
private val _seatLevel = MutableStateFlow(0)
val seatLevel: StateFlow<Int> = _seatLevelfun updateLevel(level: Int) {_seatLevel.value = level
}
- UI 层收集(DataBinding 或 Compose):
-
在传统 View 中用
LiveData
订阅:seatLevel.asLiveData().observe(...)
-
在 Jetpack Compose 中用:
val level by viewModel.seatLevel.collectAsState()
✅ SharedFlow 的典型使用(单次事件传递):
private val _event = MutableSharedFlow<UIEvent>()
val event = _event.asSharedFlow()fun showToast() {viewModelScope.launch {_event.emit(UIEvent.Toast("请填写信息"))}
}
收集事件(Compose):
LaunchedEffect(Unit) {viewModel.event.collect { event ->when (event) {is UIEvent.Toast -> showToast(event.message)}}
}
🎯 总结一句话记忆法:
StateFlow 管状态,SharedFlow 管事件。
三 进一步理解stateflow
🔍 问题 1:collect
是什么?是否相当于监听者或观察者?
是的,collect
是 Kotlin Flow 的“订阅”动作,相当于观察者(Observer)在响应数据流。
可以把 Kotlin 的 Flow
看成“冷流”(默认),就像个懒加载的发射器,只有当 collect
的时候,它才开始“流动”。
val flow = flow {emit(1)emit(2)
}flow.collect { value ->println("收到值 $value") // 相当于观察者
}
和 LiveData 一样,collect
的本质就是开始监听/接收这个流发出的数据。
🔄 问题 2:StateFlow
是不是不需要等收集者准备好才能发射数据?
正确 ✅,StateFlow 是“热流”(Hot Flow)的一种,始终保持最新值,并可以在没有收集者的情况下发射值(赋值)。
可以理解为它是一个“广播状态容器”,默认就开始运行了,是否有人收集它无所谓,它就一直存在。
val seatLevel = MutableStateFlow(0)
seatLevel.value = 3 // ✅ 可以随时赋值// 即使此时没人 collect,值仍然更新了
然后当某个地方开始 collect
,它会立即收到当前值(即最后一次 seatLevel.value 赋的值)。
🔥 所以总结:
特性 | LiveData | Flow | StateFlow |
---|---|---|---|
是否冷流 | ❌ | ✅ 是冷流 | ❌ 热流 |
是否可主动更新 | ✅ | ❌ | ✅(用 .value ) |
是否保存最后一个值 | ✅ | ❌ | ✅ |
是否需要 collect 才“运行” | ❌ | ✅ | ❌ |
是否适合状态管理 | ✅ | ❌(默认 Flow) | ✅ |
✅ 对应的代码分析:
private val seatLevel = MutableStateFlow(0)
seatLevel.value = level // ✅ 更新值,立刻广播出去(即使没人收集)
配合收集:
viewModel.seatLevel.collect { level ->updateUI(level)
}
或者传统 View 用:
viewModel.seatLevel.asLiveData().observe(...)
📌 实践建议
- 用
StateFlow
管理 ViewModel 的状态(如温度、风量、座椅等级等),它就是LiveData
的更现代替代品。 StateFlow.value = newValue
是立即生效的;即使没有收集者,也不会丢失值。- 一旦有收集者
collect
,它会立即拿到当前值,并订阅后续更新。