Kotlin 协程 Flow 的核心源码文件。这个文件主要包含 shareIn 和 stateIn 两个强大的操作符,用于将冷流(Cold Flow)转换为热流(Hot Flow)。
文件结构概览
Share.kt
├── shareIn() - 转换为 SharedFlow(共享流)
├── stateIn() - 转换为 StateFlow(状态流)
├── asSharedFlow()/asStateFlow() - 只读转换
└── onSubscription() - 订阅监听
一、shareIn - 共享流转换
核心作用
将 冷流(每次收集都重新执行)转换为 热流(多个订阅者共享同一个上游流实例)。
函数签名
public fun <T> Flow<T>.shareIn(
scope: CoroutineScope, // 协程作用域
started: SharingStarted, // 启动策略
replay: Int = 0 // 重放数量(新订阅者能收到最近几个值)
): SharedFlow<T>
三种启动策略
| 策略 | 行为 | 适用场景 |
|---|---|---|
SharingStarted.Eagerly | 立即启动,不管有没有订阅者 | 需要预加载数据,如应用启动时获取配置 |
SharingStarted.Lazily | 第一个订阅者出现时启动,无订阅者时保持缓存 | 延迟初始化,但保留最新状态 |
SharingStarted.WhileSubscribed() | 有订阅者时运行,无订阅者时停止(可配置超时) | 节省资源,如UI相关的数据流 |
使用示例
// 场景:昂贵的网络连接需要共享
class MessageRepository {
private val backendMessages: Flow<Message> = flow {
connectToBackend() // 耗时操作
while (true) {
emit(receiveMessage())
}
}
// 共享连接, eager 启动提前建立连接
val messages: SharedFlow<Message> = backendMessages
.shareIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
replay = 1 // 新订阅者收到最近1条消息
)
}
// 多个收集者共享同一个连接
viewModelScope.launch { messages.collect { /* UI 更新 */ } }
viewModelScope.launch { messages.collect { /* 日志记录 */ } }
异常处理与完成处理
backendMessages
.onCompletion { cause ->
if (cause == null) emit(CompletionSignal)
}
.catch { e ->
// 自定义异常处理
emit(ErrorMessage(e))
}
.retry { e -> e is IOException }
.shareIn(scope, SharingStarted.Eagerly)
二、stateIn - 状态流转换
核心作用
专门用于管理 状态 的共享流,总是保留最新值,类似 BehaviorSubject。
两种重载形式
1. 带初始值的 stateIn(立即返回)
public fun <T> Flow<T>.stateIn(
scope: CoroutineScope,
started: SharingStarted,
initialValue: T // 必须有初始值
): StateFlow<T>
2. 挂起式 stateIn(等待首个值)
public suspend fun <T> Flow<T>.stateIn(scope: CoroutineScope): StateFlow<T>
// 挂起直到上游发出第一个值,适合没有合适默认值的情况
使用示例
class UserViewModel : ViewModel() {
private val userIdFlow: Flow<String> = dataStore.userId
// 方式1:带初始值,立即可用
val userState: StateFlow<User> = userRepository.getUserFlow(userIdFlow)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000), // 5秒超时
initialValue = User.Loading
)
// 方式2:挂起式,等待首个真实值(在挂起函数中使用)
suspend fun getUserState(): StateFlow<User> {
return userRepository.getUserFlow(userIdFlow)
.stateIn(viewModelScope) // 挂起直到第一个用户数据到达
}
}
// UI 层使用
@Composable
fun UserScreen(viewModel: UserViewModel) {
val user by viewModel.userState.collectAsStateWithLifecycle()
// 自动管理生命周期,WhileSubscribed 确保不在前台时停止收集
}
三、内部实现机制
1. 配置融合(Operator Fusion)
private fun <T> Flow<T>.configureSharing(replay: Int): SharingConfig<T>
- 检测上游是否有
buffer、flowOn等操作符 - 智能融合,避免不必要的中间通道
2. 共享协程启动
private fun <T> CoroutineScope.launchSharing(...)
- Eagerly:
CoroutineStart.DEFAULT- 延迟到调度器可用时启动 - 其他:
CoroutineStart.UNDISPATCHED- 立即在当前协程启动,确保订阅者先注册
3. 命令处理(自定义策略)
started.command(shared.subscriptionCount)
.distinctUntilChanged()
.collectLatest { command ->
when (command) {
SharingCommand.START -> upstream.collect(shared)
SharingCommand.STOP -> { /* 取消收集 */ }
SharingCommand.STOP_AND_RESET_REPLAY_CACHE -> shared.resetReplayCache()
}
}
四、asSharedFlow / asStateFlow
将 可变流 暴露为 只读流(API 设计最佳实践):
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow() // 外部只读
private val _events = MutableSharedFlow<Event>()
val events: SharedFlow<Event> = _events.asSharedFlow()
fun updateState(newState: UiState) {
_uiState.value = newState // 内部可写
}
}
五、onSubscription - 订阅监听
public fun <T> SharedFlow<T>.onSubscription(
action: suspend FlowCollector<T>.() -> Unit
): SharedFlow<T>
执行时机:订阅者注册后,收到第一个值之前。
典型场景:发送初始事件或确认信号
messageFlow
.onSubscription {
emit(Message("Connected", System.currentTimeMillis()))
}
.shareIn(scope, SharingStarted.Lazily)
六、选型决策树
需要共享数据给多个收集者?
├── 否 → 直接使用 Flow(冷流)
└── 是 → 需要永远保留最新值?
├── 是 → 使用 stateIn(StateFlow)
│ └── UI 状态、配置数据、用户信息
└── 否 → 使用 shareIn(SharedFlow)
├── 需要事件历史?设置 replay > 0
└── 纯事件通知?replay = 0
七、关键注意事项
-
作用域生命周期:
shareIn/stateIn的 scope 决定流存活时间,通常使用viewModelScope或lifecycleScope -
内存泄漏:
WhileSubscribed策略配合replayExpirationMillis可设置缓存过期 -
线程安全:StateFlow 的
value读写是线程安全的,但compareAndSet需要处理竞争 -
背压处理:通过前置
buffer、conflate操作符配置缓冲策略
这个文件是 Kotlin 协程 Flow 共享机制的核心实现,理解它对于构建响应式 Android 应用架构至关重要。