1. StateFlow与SharedFlow概念总览
StateFlow和SharedFlow是Kotlin协程中热数据流(hot Flow)的两个关键实现,用于在组件之间安全、高效地传递状态或事件。它们都基于kotlinx.coroutines.flow构建,提供了比LiveData更灵活、更协程原生的数据共享方案。本章从概念、用途、与LiveData的对比以及必要的协程基础入手,为后续章节的深入实现打下根基。
1.1 定义与核心特性
| 特性 | StateFlow | SharedFlow |
|---|---|---|
| 类型 | 有状态的热流(持有最新值) | 无状态热流(可通过配置持有重放值) |
| 用途 | 状态持有与观察(类似LiveData but 协程原生) | 事件广播、多订阅者消息传递、背压处理 |
| 缓存 | 始终缓存当前最新值 | 可配置重放(replay)和额外缓冲(extraBufferCapacity) |
| 背压 | 无(收集者OnBufferOverflow默认SUSPEND,可配置DROP_OLDEST/DROP_LATEST) | 支持配置背压策略(SUSPEND / DROP_OLDEST / DROP_LATEST) |
| 线程安全 | 全面线程安全(内部同步) | 全面线程安全 |
| 生命周期 | 不绑定Android生命周期,但可通过viewModelScope / lifecycleScope管理 | 不绑定生命周期 |
1.2 与LiveData对比
LiveData是Android Jetpack中的生命周期感知型数据持有者,在协程出现前是状态管理的首选。StateFlow和SharedFlow的出现提供了更强大的协程集成能力。下表列出关键差异:
| 维度 | LiveData | StateFlow | SharedFlow |
|---|---|---|---|
| 协程原生 | 否(需额外转换) | 是(完全基于Flow) | 是 |
| 持有最新值 | 是(通过value) | 是(通过value) | 可配置(replay>=1时) |
| 生命周期感知 | 内置(自动停止/恢复) | 无(需手动绑定作用域) | 无 |
| 背压处理 | 无(只能丢弃旧值或阻塞主线程) | 支持(SUSPEND / DROP) | 支持,且可独立配置 |
| 多订阅者 | 支持(自动去重) | 支持(每个收集者独立收集器) | 支持(可共享同一buffer) |
| 事件一次性消费 | 不原生支持(需封装Event包装类) | 不原生支持(可通过SharedFlow重放0实现) | 原生支持(replay=0,事件只被新订阅者接收一次) |
| 测试难度 | 较低(依赖Android Arch) | 较低(纯Kotlin协程测试) | 较低 |
最佳实践: 新项目中推荐优先使用StateFlow代替LiveData进行状态管理,使用SharedFlow代替EventBus或BroadcastChannel进行一次性事件广播。迁移时可通过
.asLiveData()扩展将StateFlow转为LiveData以兼容旧代码。
1.3 协程基础回顾(Flow & Collect)
StateFlow和SharedFlow本质上是Flow接口的子类型,因此理解Flow的基本操作是必要的。
- Flow: 冷数据流,每次调用
collect时重新执行生产者代码。 - StateFlow: 热流,将
value暴露给多个收集器,收集器只会收到从订阅时刻开始的最新值(如果已有初始值)。 - SharedFlow: 热流,通过
MutableSharedFlow配置重放数、缓冲容量和背压策略,收集器可以收到历史重放值和后续新值。 - Collect: 挂起函数,在协程作用域内持续执行,直到协程取消或flow完成。
- 作用域管理: 通常使用
viewModelScope(Android)或CoroutineScope管理收集协程的生命周期。
以下为最简单StateFlow使用示例:
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow("Initial")
val uiState: StateFlow<String> = _uiState.asStateFlow()
fun updateState(newValue: String) {
_uiState.value = newValue // 线程安全更新
}
}
// 在Activity/Fragment中收集
lifecycleScope.launch {
viewModel.uiState.collect { value ->
textView.text = value
}
}
SharedFlow典型使用(一次性事件):
class EventBus {
private val _events = MutableSharedFlow<Event>(replay = 0, extraBufferCapacity = 1)
val events: SharedFlow<Event> = _events.asSharedFlow()
suspend fun emit(event: Event) {
_events.emit(event)
}
}
// 收集事件(只接收发射后的事件)
lifecycleScope.launch {
eventBus.events.collect { event ->
// 处理事件
}
}
1.4 核心概念对比图
常见错误:
- 将StateFlow用于一次性导航事件:StateFlow始终持有最新值,重复收集会导致导航事件被多次触发。此时应使用SharedFlow(replay=0)。
- 未指定SharedFlow的replay参数导致事件丢失:默认replay=0,新订阅者收不到历史数据;若需要catch-up则需要设置replay>0。
- 在不可取消的协程中使用
collect导致内存泄漏:始终绑定协程作用域(如lifecycleScope、viewModelScope)。