Skip to content

UI 事件处理:Flow vs Channel

在 Android MVVM 架构中,处理“状态 (State)”和“事件 (Events)”有着本质区别。

  • 状态: 只有最新值有意义 (UI State, User Profile)。应使用 StateFlow
  • 事件: 每一次发生都有意义 (Toast, Navigation)。应使用 ChannelSharedFlow

反模式:用 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()