Android MVI 架构中处理副作用(Side Effect) 时,该选择 MutableSharedFlow 还是 Channel,并了解它们的适用场景和核心差异?
先明确核心概念
在 MVI 中,副作用 指的是不改变 UI 状态但需要触发的操作(如导航、弹 Toast、网络请求、读写本地存储等),这类操作需要从 ViewModel 发送到 UI 层执行,而 MutableSharedFlow 和 Channel 都是 Kotlin 协程中用于跨协程通信的工具,但设计初衷和语义完全不同。
核心差异与 MVI 场景适配
1. MutableSharedFlow(推荐:绝大多数 MVI 副作用场景)
MutableSharedFlow 是热流(Hot Flow),核心特点是:
- 支持多订阅者接收数据(即使 UI 重建,重新订阅也可灵活控制是否接收历史数据);
- 可配置缓冲、重放策略(
replay)和背压策略,适配 MVI 副作用 "一次性、不重复消费" 的特点; - 与 Flow 生态深度兼容(可使用
map/filter等操作符处理副作用); - 官方已将
BroadcastChannel(多订阅者 Channel)标记为废弃,推荐用SharedFlow替代。
MVI 中推荐配置:
replay = 0:不重放历史副作用(副作用是一次性指令,页面重建后无需重复执行);extraBufferCapacity = 1:缓冲 1 个副作用(避免高频发送时丢失);onBufferOverflow = BufferOverflow.DROP_OLDEST:缓冲满时丢弃最旧的副作用(优先处理最新指令)。
2. Channel(仅适用于特定场景)
Channel 是冷流(Cold Stream),核心特点是:
- 默认一对一通信(一个发送者对应一个接收者),确保每个副作用仅被消费一次;
send()方法是挂起函数(缓冲满时会阻塞,直到接收方处理);- 语义更偏向 "任务队列",适合需要严格串行处理、消费确认的场景。
MVI 中适用场景:
- 副作用是耗时任务,且需要严格按顺序执行(如有序的数据库写入操作);
- 必须确保每个副作用都被处理,不允许丢失(如支付相关的关键指令)。
总结
- 绝大多数 MVI 副作用场景选 MutableSharedFlow:它是官方推荐方案,适配副作用 "一次性 UI 指令" 的特点,与 Flow 生态兼容更好,配置灵活且非阻塞发送。
- 仅特定场景用 Channel :需要严格串行处理、消费确认,或关键任务不允许丢失时才考虑,注意
send()是挂起函数,需处理阻塞风险。 - 核心配置建议 :SharedFlow 建议设置
replay=0、extraBufferCapacity=1,确保副作用一次性、不重复消费。