UI 事件处理:Flow vs Channel
在 Android MVVM 架构中,处理“状态 (State)”和“事件 (Events)”有着本质区别。
- 状态: 只有最新值有意义 (UI State, User Profile)。应使用
StateFlow。 - 事件: 每一次发生都有意义 (Toast, Navigation)。应使用
Channel或SharedFlow。
反模式:用 StateFlow 发事件
kotlin
// ❌ 错误示范
private val _event = MutableStateFlow<Event?>(null)
// 问题 1: 必须重置状态。否则第二次点击相同按钮可能不会触发(因为值没变)。
// 问题 2: 旋转屏幕时,Activity 重建重新订阅,会再次收到上一次的 "ShowToast" 事件。方案 A:Channel (推荐单人消费)
Channel 保证每个事件只被消费一次。非常适合“导航跳转”这种绝对不能重复执行的操作。
单播特性 (Unicast)
receiveAsFlow() 产生的流是单播的。如果你在多个地方同时 collect 同一个 Channel Flow,事件会被它们瓜分(谁抢到算谁的),而不是广播。 如果你需要多个组件同时响应同一个事件,请使用方案 B (SharedFlow)。
kotlin
// ViewModel
private val _events = Channel<Event>(Channel.BUFFERED)
val events = _events.receiveAsFlow() // 转为 Flow 给 UI 观察
// Activity
lifecycleScope.launch {
viewModel.events
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collect { event ->
// ✅ 即使旋转屏幕,也不会重复收到旧消息
when(event) { ... }
}
}方案 B:SharedFlow (广播)
如果你的事件需要被多个观察者同时收到(例如:多处 UI 同时弹窗),使用 SharedFlow。
kotlin
// replay=0 意味着没有粘性,新订阅者不会收到旧消息
private val _events = MutableSharedFlow<Event>(replay = 0)
val events = _events.asSharedFlow()