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 属于热流,并且是立即发射的

写的:

kotlin 复制代码
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,则:

kotlin 复制代码
val current = levelFlow.first() // 这是"立即获取当前值",是同步挂起行为

在这种情况下:

  • first() 会立即得到值(因为 StateFlow 始终有值);
  • 不会无限挂起;
  • ✅ 所以 当前的用法在 ViewModel 中是安全的,不会存在性能/延迟问题。

⚠️ 注意两个潜在坑点

❶ 若未来换成 SharedFlow,first() 会挂起!

SharedFlow 不保证有初始值,因此:

kotlin 复制代码
val flow = MutableSharedFlow<Int>()
val current = flow.first() // ❌ 会挂起,直到有值发射!

🔧 若用 SharedFlow,应使用:

kotlin 复制代码
flow.firstOrNull() ?: 0  // 或者使用 timeout 保护

❷ 若 map 中 StateFlow 没有预先创建,getValue() 会抛异常

现在:

kotlin 复制代码
fun observeFanLevel(position: SeatPosition): StateFlow<Int> = _fanLevels.getValue(position)

这要求 _fanLevels[position] 必须事先创建,否则会抛出 NoSuchElementException

✅ 如果在初始化阶段或通过 putIfAbsent 保证了 MutableStateFlow 已初始化,那没问题。

⚠️ 否则建议使用更安全方式:

kotlin 复制代码
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 的状态数据绑定
kotlin 复制代码
val uiState = MutableStateFlow(UiState.Loading)

fun loadData() {
    viewModelScope.launch {
        val result = repository.fetch()
        uiState.value = UiState.Success(result)
    }
}

优点

  • 始终保存最新状态
  • 配合 DataBinding 或 Jetpack Compose 非常适合 UI 渲染

📌 SharedFlow 适合:一次性事件(如 Toast、导航)
kotlin 复制代码
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 是一次"广播"
  • 多个收集器可以都收到该事件
  • 配合 LaunchedEffectcollect 适合 Compose 或传统监听回调

🚦 常见使用建议和实践

✅ StateFlow 的典型使用(MVVM 架构):

  1. ViewModel 层
kotlin 复制代码
private val _seatLevel = MutableStateFlow(0)
val seatLevel: StateFlow<Int> = _seatLevel

fun updateLevel(level: Int) {
    _seatLevel.value = level
}
  1. UI 层收集(DataBinding 或 Compose)
  • 在传统 View 中用 LiveData 订阅:

    kotlin 复制代码
    seatLevel.asLiveData().observe(...)
  • 在 Jetpack Compose 中用:

    kotlin 复制代码
    val level by viewModel.seatLevel.collectAsState()

✅ SharedFlow 的典型使用(单次事件传递):

kotlin 复制代码
private val _event = MutableSharedFlow<UIEvent>()
val event = _event.asSharedFlow()

fun showToast() {
    viewModelScope.launch {
        _event.emit(UIEvent.Toast("请填写信息"))
    }
}

收集事件(Compose):

kotlin 复制代码
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 的时候,它才开始"流动"。

kotlin 复制代码
val flow = flow {
    emit(1)
    emit(2)
}

flow.collect { value ->
    println("收到值 $value")  // 相当于观察者
}

和 LiveData 一样,collect 的本质就是开始监听/接收这个流发出的数据。


🔄 问题 2:StateFlow 是不是不需要等收集者准备好才能发射数据?

正确 ✅,StateFlow 是"热流" (Hot Flow)的一种,始终保持最新值 ,并可以在没有收集者的情况下发射值(赋值)

可以理解为它是一个"广播状态容器",默认就开始运行了,是否有人收集它无所谓,它就一直存在。

kotlin 复制代码
val seatLevel = MutableStateFlow(0)
seatLevel.value = 3  // ✅ 可以随时赋值

// 即使此时没人 collect,值仍然更新了

然后当某个地方开始 collect,它会立即收到当前值(即最后一次 seatLevel.value 赋的值)


🔥 所以总结:

特性 LiveData Flow StateFlow
是否冷流 ✅ 是冷流 ❌ 热流
是否可主动更新 ✅(用 .value
是否保存最后一个值
是否需要 collect 才"运行"
是否适合状态管理 ❌(默认 Flow)

✅ 对应的代码分析:

kotlin 复制代码
private val seatLevel = MutableStateFlow(0)
seatLevel.value = level  // ✅ 更新值,立刻广播出去(即使没人收集)

配合收集:

kotlin 复制代码
viewModel.seatLevel.collect { level ->
    updateUI(level)
}

或者传统 View 用:

kotlin 复制代码
viewModel.seatLevel.asLiveData().observe(...)

📌 实践建议

  • StateFlow 管理 ViewModel 的状态(如温度、风量、座椅等级等),它就是 LiveData 的更现代替代品。
  • StateFlow.value = newValue 是立即生效的;即使没有收集者,也不会丢失值。
  • 一旦有收集者 collect,它会立即拿到当前值,并订阅后续更新。

相关推荐
一只柠檬新10 小时前
Web和Android的渐变角度区别
android
志旭10 小时前
从0到 1实现BufferQueue GraphicBuffer fence HWC surfaceflinger
android
_一条咸鱼_10 小时前
Android Runtime堆内存架构设计(47)
android·面试·android jetpack
用户20187928316710 小时前
WMS(WindowManagerService的诞生
android
用户20187928316710 小时前
通俗易懂的讲解:Android窗口属性全解析
android
openinstall10 小时前
A/B测试如何借力openinstall实现用户价值深挖?
android·ios·html
二流小码农10 小时前
鸿蒙开发:资讯项目实战之项目初始化搭建
android·ios·harmonyos
志旭11 小时前
android15 vsync源码分析
android
志旭11 小时前
Android 14 HWUI 源码研究 View Canvas RenderThread ViewRootImpl skia
android
whysqwhw12 小时前
Egloo 高级用法
android