在 Kotlin 的协程库(kotlinx.coroutines.flow
)中,MutableStateFlow
和 MutableSharedFlow
都是用于构建响应式数据流的可变(Mutable)热流(Hot Flow),但它们的设计目标和行为特性有显著区别。以下是它们的核心对比:
1. 核心区别总结
特性 | MutableStateFlow |
MutableSharedFlow |
---|---|---|
数据保留 | 始终保存最新一个值(必须有初始值) | 不保留值(默认),但可配置缓冲区保留历史值 |
订阅时机 | 新订阅者立即收到当前最新值 | 新订阅者默认不接收历史值(除非配置replay ) |
背压处理 | 通过覆盖最新值自动处理 | 可配置缓冲区大小或策略(如BufferOverflow ) |
使用场景 | 状态管理(如UI状态) | 事件处理(如用户操作、通知) |
2. 详细行为对比
(1)数据存储与回放
-
MutableStateFlow
-
必须通过构造函数指定初始值:
kotlinval state = MutableStateFlow(initialValue = 0) // 必须提供初始值
-
始终保存最新一个值 ,新订阅者会立即获取该值:
kotlinstate.collect { println("Collector 1: $it") } // 立即打印当前值 state.value = 1 state.collect { println("Collector 2: $it") } // 立即打印1
-
-
MutableSharedFlow
-
无需初始值,默认不保留任何值(除非配置
replay
):kotlinval shared = MutableSharedFlow<Int>() // 无初始值
-
通过
replay
参数控制新订阅者接收的历史值数量:kotlinval shared = MutableSharedFlow<Int>(replay = 2) // 保留最近2个值 shared.tryEmit(1) shared.tryEmit(2) shared.collect { println("Collector: $it") } // 打印1, 2(历史值)
-
(2)发射(Emit)行为
-
MutableStateFlow
-
通过
.value
直接更新值(并发安全):kotlinstate.value = newValue // 等同于state.tryEmit(newValue)
-
去重优化 :如果新值与当前值相同(
equals
为true
),不会触发下游收集。
-
-
MutableSharedFlow
-
必须显式调用
emit
或tryEmit
:kotlinshared.tryEmit(event) // 非挂起函数 // 或 launch { shared.emit(event) } // 挂起函数,可能被暂停
-
无去重:即使发送相同值,也会触发下游收集。
-
(3)背压(Backpressure)处理
-
MutableStateFlow
- 自动处理背压:新值直接覆盖旧值,下游永远收到最新值。
-
MutableSharedFlow
-
可配置缓冲区大小和溢出策略:
kotlinMutableSharedFlow<Int>( extraBufferCapacity = 10, // 缓冲区大小 onBufferOverflow = BufferOverflow.DROP_OLDEST // 溢出时丢弃旧值 )
-
3. 典型使用场景
MutableStateFlow
-
状态管理 :维护单一可变状态,如UI状态、全局配置。
kotlin// ViewModel中管理UI状态 private val _uiState = MutableStateFlow<UiState>(Loading) val uiState: StateFlow<UiState> = _uiState.asStateFlow() fun loadData() { _uiState.value = Loading _uiState.value = Success(data) }
MutableSharedFlow
-
事件通知 :处理一次性事件(如按钮点击、错误消息)。
kotlin// 单次事件通知(无replay) private val _events = MutableSharedFlow<Event>() val events = _events.asSharedFlow() fun onButtonClick() { viewModelScope.launch { _events.emit(ClickEvent) } } // 收集端需要处理重复消费问题 events.collect { event -> // 每次emit都会触发 }
4. 关键选择建议
- 需要维护当前状态 ? → 选
StateFlow
- 需要广播事件 且不关心历史值? → 选
SharedFlow
(replay = 0
) - 需要事件重放 (如页面恢复时重新处理事件)? → 选
SharedFlow
(replay > 0
) - 需要高性能无阻塞发射 ? → 选
SharedFlow
(tryEmit
非挂起)
5. 补充注意事项
-
StateFlow
是SharedFlow
的特例以下代码实现等价:
kotlinval stateFlow = MutableStateFlow(initialValue) // 等价于 val sharedFlow = MutableSharedFlow( replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST ).also { it.tryEmit(initialValue) }
-
线程安全性
两者均可在多线程环境中安全使用(内部已实现同步机制)。
-
生命周期感知
在Android中,通常配合
Lifecycle.repeatOnLifecycle
避免泄漏:kotlinlifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { stateFlow.collect { updateUI(it) } } }