Skip to content

one-shot-event-demo ​

1. 实验目标 ​

验证“提交成功 Toast / 导航 / 支付回调”这类一次性事件,为什么不能直接塞进持久 uiState:

  • uiState 只承载可重绘状态:isSubmitting、submitCount、lastResult
  • MutableSharedFlow 单独承载一次性事件:Toast / 导航 / 支付结果
  • repeatOnLifecycle 保证页面回到前台时重新订阅,但不会平白重放旧事件

2. 对应事故样板 ​

  • 为什么成功 Toast 会在旋转屏幕后重复弹?
    • 一句话答:因为把一次性事件升级成了可恢复 state,页面重建后又按“最新状态”重新消费了一次。
  • 为什么导航结果不该塞进 SavedStateHandle?
    • 一句话答:导航结果是脉冲,不是事实;恢复时应重建页面状态,而不是重放旧事件。

3. 运行方式 ​

bash
cd labs/android/host
./gradlew installDebug

打开 Android Host → 打开 one-shot event 实验室。

4. 观察步骤 ​

  1. 点击“模拟提交成功”
  2. 观察页面状态区:submitCount 与 lastResult 更新
  3. 观察 Toast:只弹一次,并在事件日志区留下最近一次消息
  4. 旋转屏幕或退后台再回来:页面状态保留,但旧 Toast 不会再次消费

5. 关键实现速览 ​

5.1 State 与 Event 分通道 ​

kotlin
private val _uiState = MutableStateFlow(OneShotUiState())
private val _events = MutableSharedFlow<UiEvent>()

这段代码说明:State 是“当前事实”,Event 是“一次性脉冲”,通道必须拆开。

完整源码:OneShotEventDemoActivity.kt

5.2 事件只在收集时消费一次 ​

kotlin
viewModelScope.launch {
    _events.emit(UiEvent.ShowToast("提交成功,只消费一次 #$nextCount"))
}

这段代码说明:一次性事件应由事件流主动发射,而不是靠重绘时读取旧字段触发副作用。

完整源码:OneShotEventDemoActivity.kt

6. 对应知识库文档 ​

站点构建时间:2026/8/24 23:43:17