概念差异
- StateFlow:是一个状态持有者,它可以持有一个状态值,并将该值的变化通知给它的收集器。StateFlow始终需要一个初始值,因为它代表了一个状态,即使在没有数据更新时,也有一个默认状态。
- SharedFlow:是一个热流,它可以将多个值发送给多个收集器。它并不持有状态,只是负责发送数据。
特性差异
- StateFlow:
- 初始值:必须有初始值。
- 一致性:收集器总是能收到最新的值,即使在订阅时新值还未产生,也会收到初始值。
- 重放:会向新的收集器重放最后一个值。
- SharedFlow:
- 初始值:不需要初始值。
- 一致性:新的收集器不会自动收到之前的值,除非配置了重放缓存。
- 重放:可以通过配置重放缓存大小,决定向新收集器重放多少个之前的值。
使用场景差异
- 优先选择StateFlow的场景:
- 当需要表示一个状态时,例如界面的加载状态(loading、success、error),因为StateFlow始终有一个当前状态值。
- 当希望收集器总是能获取到最新状态,即使在订阅时该状态未改变,比如用户登录状态,新订阅者应立即知道当前登录状态。
- 示例代码:
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class LoginViewModel {
private val _isLoggedIn = MutableStateFlow(false)
val isLoggedIn: StateFlow<Boolean> = _isLoggedIn
fun login() {
// 模拟登录操作
_isLoggedIn.value = true
}
}
fun main() = runBlocking {
val viewModel = LoginViewModel()
launch {
viewModel.isLoggedIn.collect { isLoggedIn ->
println("Is logged in: $isLoggedIn")
}
}
viewModel.login()
}
- 优先选择SharedFlow的场景:
- 当需要发送事件流,且不需要收集器接收历史事件,例如按钮点击事件,新订阅者不需要知道之前的点击情况。
- 当希望控制向新收集器重放多少个历史值时,比如实时聊天消息,可配置重放缓存让新订阅者获取最近几条消息。
- 示例代码:
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class ButtonClickViewModel {
private val _clickFlow = MutableSharedFlow<Unit>()
val clickFlow: SharedFlow<Unit> = _clickFlow
fun onButtonClick() {
launch {
_clickFlow.emit(Unit)
}
}
}
fun main() = runBlocking {
val viewModel = ButtonClickViewModel()
launch {
viewModel.clickFlow.collect {
println("Button clicked")
}
}
viewModel.onButtonClick()
}