Android SharedFlow 详解
一、引言
在 Android 开发中,响应式编程是一种高效处理异步数据流的方式。Kotlin 协程提供的 SharedFlow 作为热流(Hot Flow)的代表,在事件广播和多订阅者场景中发挥着重要作用。本文将从概念、特性、使用场景及实践等方面全面解析 SharedFlow,帮助大家深入理解并灵活运用这一工具。
二、基本概念
2.1 定义与作用
SharedFlow 是 Kotlin 协程库中的一个核心组件,用于在多个订阅者之间共享事件或数据流。它属于热流,意味着数据一旦被发射,即使没有订阅者监听也会立即发送。这种特性使其特别适合处理一次性事件,如导航指令、弹窗通知、Toast 消息等。
2.2 与 StateFlow 的对比
特性 | SharedFlow | StateFlow |
本质 | 事件广播器,无初始值 | 状态持有者,必须有初始值 |
热流特性 | 数据立即发射,无需等待订阅者 | 数据立即发射,无需等待订阅者 |
数据存储 | 不存储状态,可配置历史数据回放 | 存储当前状态,自动缓存最新值 |
适用场景 | 事件通知、一次性操作 | UI 状态管理、实时数据同步 |
构造参数 | replay、extraBufferCapacity、onBufferOverflow | 初始值 value |
关键区别:
- StateFlow 是 SharedFlow 的特殊形式(相当于 replay=1 的 SharedFlow),强制要求初始值并自动缓存最新状态。
- SharedFlow 更灵活,适合无状态的事件广播,而 StateFlow 专注于状态管理。
三、核心特性与参数配置
3.1 热流特性
SharedFlow 在创建后立即开始发射数据,即使没有订阅者。当订阅者加入时,可通过 replay 参数配置接收最近的历史数据。例如:
val sharedFlow = MutableSharedFlow<Int>(replay = 2) // 缓存最近 2 条数据 |
3.2 关键参数详解
3.2.1 replay
- 作用:控制新订阅者可接收的历史数据数量。
- 示例:
val sharedFlow = MutableSharedFlow<Int>(replay = 1) // 新订阅者接收最近 1 条数据 |
若 replay = 0(默认值),新订阅者仅接收订阅后的新数据。
3.2.2 extraBufferCapacity
- 作用:扩展缓冲区容量,用于存储订阅者未及时消费的数据。
- 示例:
val sharedFlow = MutableSharedFlow<Int>(extraBufferCapacity = 3) // 总缓冲区大小为 replay + 3 |
结合 replay,总缓冲区大小为 replay + extraBufferCapacity。
3.2.3 onBufferOverflow
- 作用:定义缓冲区溢出时的处理策略。
- 选项:
- SUSPEND(默认):挂起发射者,直到缓冲区有空间。
- DROP_OLDEST:丢弃最旧数据,腾出空间。
- DROP_LATEST:丢弃最新数据,保留旧数据。
- 示例:
val sharedFlow = MutableSharedFlow<Int>( onBufferOverflow = BufferOverflow.DROP_OLDEST ) |
当缓冲区满时,丢弃最早的数据以接收新数据。
3.3 背压处理
SharedFlow 通过 onBufferOverflow 策略处理背压(Backpressure),即生产者速度超过消费者时的应对机制。例如:
// 当缓冲区满时,丢弃最新发射的数据 val sharedFlow = MutableSharedFlow<Int>( extraBufferCapacity = 2, onBufferOverflow = BufferOverflow.DROP_LATEST ) |
此配置适用于实时数据更新场景,确保最新数据优先传递。
四、使用场景与实践
4.1 ViewModel 中的事件管理
在 ViewModel 中定义 SharedFlow 以发射 UI 事件(如网络请求结果、用户操作反馈):
class MainViewModel : ViewModel() { private val _eventFlow = MutableSharedFlow<String>() val eventFlow: SharedFlow<String> = _eventFlow.asSharedFlow() fun fetchData() { viewModelScope.launch { // 模拟耗时操作 delay(1000) _eventFlow.emit("Data fetched successfully") } } } // 在 Activity 中监听事件 lifecycleScope.launch { viewModel.eventFlow.collect { event -> Toast.makeText(this@MainActivity, event, Toast.LENGTH_SHORT).show() } } |
4.2 Fragment 间通信
通过共享 ViewModel 和 SharedFlow 在 Fragment 间传递数据:
// SharedViewModel.kt class SharedViewModel : ViewModel() { private val _sharedFlow = MutableSharedFlow<String>() val sharedFlow: SharedFlow<String> = _sharedFlow fun sendData(data: String) { viewModelScope.launch { _sharedFlow.emit(data) } } } // 发送 Fragment class SendingFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val sharedViewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java) sharedViewModel.sendData("Hello from SendingFragment") } } // 接收 Fragment class ReceivingFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val sharedViewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java) lifecycleScope.launch { sharedViewModel.sharedFlow.collect { data -> textView.text = data } } } } |
4.3 事件总线实现
构建全局事件总线,支持多组件订阅:
class EventBus { private val _events = MutableSharedFlow<Event>(replay = 1) val events: SharedFlow<Event> = _events.asSharedFlow() suspend fun send(event: Event) { _events.emit(event) } } sealed class Event { object NetworkConnected : Event() data class Error(val message: String) : Event() } // 使用示例 val eventBus = EventBus() // 订阅事件 lifecycleScope.launch { eventBus.events.collect { event -> when (event) { is Event.NetworkConnected -> showToast("Network connected") is Event.Error -> showErrorDialog(event.message) } } } // 发送事件 viewModelScope.launch { eventBus.send(Event.NetworkConnected) } |
五、注意事项与最佳实践
5.1 生命周期管理
- 使用 lifecycleScope.launch 启动收集协程,确保与宿主生命周期同步。
- 在 ViewModel 中使用 viewModelScope,避免内存泄漏。
5.2 错误处理
- 添加 catch 操作符处理流中的异常:
lifecycleScope.launch { viewModel.eventFlow .catch { e -> handleError(e) } .collect { event -> updateUI(event) } } |
5.3 性能优化
- 合理配置 replay 和 extraBufferCapacity,避免内存浪费。
- 根据场景选择背压策略:
- SUSPEND 适用于不能丢失数据的场景(如金融交易)。
- DROP_OLDEST 或 DROP_LATEST 适用于实时更新(如聊天消息)。
5.4 避免粘性事件
- 若不需要历史数据,设置 replay = 0,确保新订阅者仅接收订阅后的事件。
六、总结
SharedFlow 作为 Kotlin 协程中的热流工具,在事件广播和多订阅者场景中表现出色。通过灵活配置 replay、extraBufferCapacity 和 onBufferOverflow,开发者可以高效管理数据流,避免背压问题。结合 ViewModel 和生命周期感知组件,SharedFlow 能显著提升代码的可维护性和响应性能。在实际开发中,需根据具体场景选择 SharedFlow 或 StateFlow,并遵循最佳实践以确保稳定性和效率。