Kotlin Flow 完整使用指南
目录
- [Flow 基础](#Flow 基础)
- [Flow 操作符详解](#Flow 操作符详解)
- [StateFlow vs SharedFlow](#StateFlow vs SharedFlow)
- [Compose 中的 Flow 使用](#Compose 中的 Flow 使用)
- 状态与流的转换
- 实战最佳实践
Flow 基础
什么是 Flow?
Flow 是 Kotlin 协程库中的冷流(Cold Stream),用于异步返回多个值的序列。
kotlin
// 创建一个简单的 Flow
val simpleFlow = flow {
emit(1)
delay(100)
emit(2)
delay(100)
emit(3)
}
// 收集 Flow
simpleFlow.collect { value ->
println(value) // 输出: 1, 2, 3
}
Flow 的特点
- ✅ 冷流:只有被收集时才会执行
- ✅ 异步:支持挂起函数
- ✅ 顺序:按顺序发射值
- ✅ 可取消:支持协程取消
- ✅ 异常安全:内置异常处理
Flow 的三个角色
kotlin
flow { // 1️⃣ 生产者(Builder)
emit(1)
}
.map { it * 2 } // 2️⃣ 中间操作符(Intermediate)
.collect { // 3️⃣ 消费者(Terminal)
println(it)
}
Flow 操作符详解
1. 创建操作符
flow { } - 基础构建器
kotlin
val myFlow = flow {
emit(1)
delay(100)
emit(2)
}
flowOf() - 固定值
kotlin
val fixedFlow = flowOf(1, 2, 3, 4, 5)
asFlow() - 集合转 Flow
kotlin
val listFlow = listOf(1, 2, 3).asFlow()
val rangeFlow = (1..5).asFlow()
channelFlow { } - 并发发射
kotlin
val channelFlow = channelFlow {
launch { send(1) }
launch { send(2) }
}
callbackFlow { } - 回调转 Flow
kotlin
fun listenToSensor() = callbackFlow {
val listener = object : SensorListener {
override fun onData(data: Int) {
trySend(data)
}
}
sensor.registerListener(listener)
awaitClose { sensor.unregisterListener(listener) }
}
2. 转换操作符
map - 一对一转换
kotlin
flowOf(1, 2, 3)
.map { it * 2 }
.collect { println(it) } // 2, 4, 6
mapNotNull - 转换并过滤 null
kotlin
flowOf(1, null, 3)
.mapNotNull { it }
.collect { println(it) } // 1, 3
transform - 自定义转换(可发射多个值)
kotlin
flowOf(1, 2)
.transform { value ->
emit(value) // 发射原值
emit(value * 10) // 再发射一个转换值
}
.collect { println(it) } // 1, 10, 2, 20
scan - 累积转换
kotlin
flowOf(1, 2, 3)
.scan(0) { acc, value -> acc + value }
.collect { println(it) } // 0, 1, 3, 6
3. flatMap 系列(流的转换)
flatMapConcat - 顺序展平
kotlin
flowOf(1, 2)
.flatMapConcat { value ->
flow {
emit("A$value")
delay(100)
emit("B$value")
}
}
.collect { println(it) } // A1, B1, A2, B2
flatMapMerge - 并发展平
kotlin
flowOf(1, 2)
.flatMapMerge { value ->
flow {
delay(100 - value * 10)
emit("A$value")
}
}
.collect { println(it) } // A2, A1(并发)
flatMapLatest - 保留最新(⭐ 常用)
kotlin
flow {
emit(1)
delay(50)
emit(2) // 取消前一个
}
.flatMapLatest { value ->
flow {
delay(100)
emit("A$value")
}
}
.collect { println(it) } // 只有 A2
使用场景:
- 🔍 搜索(取消旧的搜索请求)
- 🔄 实时数据刷新
- 📱 页面切换(取消旧页面的数据加载)
4. 过滤操作符
filter - 条件过滤
kotlin
flowOf(1, 2, 3, 4, 5)
.filter { it % 2 == 0 }
.collect { println(it) } // 2, 4
filterNot - 反向过滤
kotlin
flowOf(1, 2, 3, 4, 5)
.filterNot { it % 2 == 0 }
.collect { println(it) } // 1, 3, 5
filterNotNull - 过滤 null
kotlin
flowOf(1, null, 3)
.filterNotNull()
.collect { println(it) } // 1, 3
distinctUntilChanged - 去除连续重复
kotlin
flowOf(1, 1, 2, 2, 3, 1)
.distinctUntilChanged()
.collect { println(it) } // 1, 2, 3, 1
debounce - 防抖(⭐ 常用于搜索)
kotlin
searchQueryFlow
.debounce(300) // 等待 300ms 无新输入
.collect { query ->
performSearch(query)
}
sample - 采样(固定时间间隔取最新值)
kotlin
flow {
repeat(10) {
emit(it)
delay(50)
}
}
.sample(150) // 每 150ms 采样一次
.collect { println(it) } // 2, 5, 8
5. 组合操作符
combine - 合并多个流(⭐ 最常用)
kotlin
val flow1 = flowOf(1, 2, 3)
val flow2 = flowOf("A", "B", "C")
combine(flow1, flow2) { num, str ->
"$str$num"
}
.collect { println(it) } // 任意一个流发射都会触发组合
// 多个流的组合
combine(userFlow, settingsFlow, notificationsFlow) { user, settings, notifications ->
HomeScreenState(user, settings, notifications)
}
zip - 配对(一对一)
kotlin
val flow1 = flowOf(1, 2, 3)
val flow2 = flowOf("A", "B")
flow1.zip(flow2) { num, str ->
"$str$num"
}
.collect { println(it) } // A1, B2(只配对两次)
merge - 合并(先到先得)
kotlin
val flow1 = flow { delay(100); emit(1) }
val flow2 = flow { delay(50); emit(2) }
merge(flow1, flow2)
.collect { println(it) } // 2, 1(按时间顺序)
6. 限制操作符
take - 只取前 N 个
kotlin
flowOf(1, 2, 3, 4, 5)
.take(3)
.collect { println(it) } // 1, 2, 3
takeWhile - 条件满足时继续取
kotlin
flowOf(1, 2, 3, 4, 5)
.takeWhile { it < 4 }
.collect { println(it) } // 1, 2, 3
drop - 跳过前 N 个
kotlin
flowOf(1, 2, 3, 4, 5)
.drop(2)
.collect { println(it) } // 3, 4, 5
dropWhile - 条件满足时跳过
kotlin
flowOf(1, 2, 3, 4, 5)
.dropWhile { it < 3 }
.collect { println(it) } // 3, 4, 5
7. 异常处理
catch - 捕获上游异常(⭐ 重要)
kotlin
flow {
emit(1)
throw Exception("Error!")
}
.catch { e ->
println("Caught: ${e.message}")
emit(-1) // 可以发射默认值
}
.collect { println(it) } // 1, -1
注意: catch 只能捕获上游的异常!
kotlin
flow { emit(1) }
.map { throw Exception("Error in map") }
.catch { emit(-1) } // ✅ 可以捕获
.collect { throw Exception("Error in collect") } // ❌ 无法捕获
retry - 重试
kotlin
var attempt = 0
flow {
attempt++
if (attempt < 3) throw Exception("Fail $attempt")
emit("Success")
}
.retry(3) // 最多重试 3 次
.collect { println(it) }
retryWhen - 条件重试
kotlin
flow {
throw IOException("Network error")
}
.retryWhen { cause, attempt ->
if (cause is IOException && attempt < 3) {
delay(1000 * attempt) // 指数退避
true // 重试
} else {
false // 不重试
}
}
.collect()
8. 生命周期操作符
onStart - 流开始时
kotlin
flowOf(1, 2, 3)
.onStart {
println("Flow started")
emit(0) // 可以在开始前发射值
}
.collect { println(it) } // 0, 1, 2, 3
onCompletion - 流完成时
kotlin
flowOf(1, 2, 3)
.onCompletion { cause ->
if (cause == null) println("Completed successfully")
else println("Completed with error: $cause")
}
.collect { println(it) }
onEach - 每个元素发射前
kotlin
flowOf(1, 2, 3)
.onEach { println("About to emit: $it") }
.collect { println("Collected: $it") }
onEmpty - 流为空时
kotlin
emptyFlow<Int>()
.onEmpty { emit(-1) }
.collect { println(it) } // -1
9. 终端操作符(收集)
collect - 基础收集
kotlin
flow.collect { value ->
println(value)
}
collectLatest - 只处理最新值
kotlin
flow {
emit(1)
delay(100)
emit(2)
}
.collectLatest { value ->
delay(200) // 处理时间
println(value) // 只打印 2(1 被取消)
}
toList / toSet - 转换为集合
kotlin
val list = flowOf(1, 2, 3).toList() // [1, 2, 3]
val set = flowOf(1, 1, 2).toSet() // {1, 2}
first / firstOrNull - 获取第一个
kotlin
val first = flowOf(1, 2, 3).first() // 1
val firstOrNull = emptyFlow<Int>().firstOrNull() // null
single - 获取唯一值
kotlin
val single = flowOf(1).single() // 1
// flowOf(1, 2).single() // 抛出异常
reduce / fold - 归约
kotlin
val sum = flowOf(1, 2, 3).reduce { acc, value -> acc + value } // 6
val product = flowOf(1, 2, 3).fold(10) { acc, value -> acc * value } // 60
10. 上下文切换
flowOn - 更改上游的调度器(⭐ 重要)
kotlin
flow {
println("Emitting on: ${Thread.currentThread().name}")
emit(1)
}
.flowOn(Dispatchers.IO) // 上游在 IO 线程
.map {
println("Mapping on: ${Thread.currentThread().name}")
it * 2
}
.collect { // 在调用者的上下文
println("Collecting on: ${Thread.currentThread().name}")
}
buffer - 缓冲(提高性能)
kotlin
flow {
repeat(3) {
delay(100)
emit(it)
}
}
.buffer() // 生产者不等待消费者
.collect {
delay(300)
println(it)
}
conflate - 合并(跳过中间值)
kotlin
flow {
repeat(10) {
delay(100)
emit(it)
}
}
.conflate() // 消费慢时跳过中间值
.collect {
delay(300)
println(it) // 可能只打印 0, 3, 6, 9
}
StateFlow vs SharedFlow
对比表
| 特性 | StateFlow | SharedFlow |
|---|---|---|
| 类型 | 热流 | 热流 |
| 初始值 | 必须有 | 可选 |
| 重放 | 永远是 1 | 可配置(0-N) |
| 去重 | ✅ 自动去重 | ❌ 不去重 |
| 用途 | 状态管理 | 事件传递 |
| Compose | collectAsState() |
collectAsState() |
StateFlow - 状态管理
kotlin
// 1️⃣ 创建 StateFlow
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun updateState() {
_uiState.value = UiState(data = "New data")
// 或使用 update
_uiState.update { it.copy(data = "New data") }
}
}
// 2️⃣ 在 Compose 中使用
@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
val uiState by viewModel.uiState.collectAsState()
Text(text = uiState.data)
}
特点:
- ✅ 永远有值(初始值)
- ✅ 自动去重(相同值不会通知)
- ✅ 新订阅者立即获得当前值
- ✅ 适合 UI 状态管理
SharedFlow - 事件传递
kotlin
// 1️⃣ 创建 SharedFlow
class MyViewModel : ViewModel() {
private val _events = MutableSharedFlow<UiEvent>()
val events: SharedFlow<UiEvent> = _events.asSharedFlow()
fun sendEvent() {
viewModelScope.launch {
_events.emit(UiEvent.ShowToast("Hello"))
}
}
}
// 2️⃣ 在 Compose 中使用
@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
val context = LocalContext.current
LaunchedEffect(Unit) {
viewModel.events.collect { event ->
when (event) {
is UiEvent.ShowToast -> {
Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
}
}
}
}
}
特点:
- ✅ 可配置重放数量
- ✅ 不去重(相同事件也会发送)
- ✅ 适合一次性事件(导航、Toast)
- ⚠️ 新订阅者不会获得历史值(除非配置 replay)
配置 SharedFlow
kotlin
MutableSharedFlow<Event>(
replay = 1, // 重放最近 1 个事件给新订阅者
extraBufferCapacity = 64, // 额外缓冲区
onBufferOverflow = BufferOverflow.DROP_OLDEST // 缓冲区满时策略
)
Compose 中的 Flow 使用
1. collectAsState - 转换为 State
kotlin
@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
// 基础用法
val uiState by viewModel.uiState.collectAsState()
// 指定初始值
val count by viewModel.countFlow.collectAsState(initial = 0)
Text(text = "Count: $count")
}
2. collectAsStateWithLifecycle - 生命周期感知(⭐ 推荐)
kotlin
@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
// 自动在 STARTED 时开始收集,STOPPED 时停止
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
// 自定义生命周期
val data by viewModel.dataFlow.collectAsStateWithLifecycle(
minActiveState = Lifecycle.State.RESUMED
)
Text(text = uiState.data)
}
优势:
- ✅ 节省资源(后台时停止收集)
- ✅ 避免后台崩溃
- ✅ 更好的性能
3. LaunchedEffect - 副作用收集
kotlin
@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
val context = LocalContext.current
// 收集一次性事件
LaunchedEffect(Unit) {
viewModel.events.collect { event ->
when (event) {
is Event.Navigate -> {
// 导航
}
is Event.ShowToast -> {
Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
}
}
}
}
}
4. produceState - 转换任意数据源为 State
kotlin
@Composable
fun loadNetworkImage(url: String): State<Resource<Image>> {
return produceState<Resource<Image>>(initialValue = Resource.Loading) {
value = try {
Resource.Success(loadImage(url))
} catch (e: Exception) {
Resource.Error(e)
}
}
}
// 使用
@Composable
fun ImageScreen() {
val imageResource by loadNetworkImage("https://example.com/image.jpg")
when (imageResource) {
is Resource.Loading -> CircularProgressIndicator()
is Resource.Success -> Image(imageResource.data)
is Resource.Error -> Text("Error: ${imageResource.error}")
}
}
5. snapshotFlow - Compose State 转 Flow
kotlin
@Composable
fun SearchScreen() {
var searchQuery by remember { mutableStateOf("") }
LaunchedEffect(Unit) {
snapshotFlow { searchQuery }
.debounce(300)
.filter { it.isNotEmpty() }
.collectLatest { query ->
// 执行搜索
}
}
TextField(
value = searchQuery,
onValueChange = { searchQuery = it }
)
}
6. derivedStateOf - 派生状态
kotlin
@Composable
fun ListScreen(items: List<Item>) {
val listState = rememberLazyListState()
// 派生状态,只在需要时重组
val isScrollingUp by remember {
derivedStateOf {
listState.firstVisibleItemIndex > 0
}
}
AnimatedVisibility(visible = !isScrollingUp) {
FloatingActionButton(onClick = { }) {
Icon(Icons.Default.Add, null)
}
}
}
状态与流的转换
Flow → StateFlow
kotlin
// 方法1: stateIn(推荐)
val stateFlow: StateFlow<Int> = flow { emit(1) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = 0
)
// 方法2: MutableStateFlow 手动收集
class MyViewModel : ViewModel() {
private val _state = MutableStateFlow(0)
val state: StateFlow<Int> = _state
init {
viewModelScope.launch {
flow { emit(1) }.collect { value ->
_state.value = value
}
}
}
}
Flow → SharedFlow
kotlin
val sharedFlow: SharedFlow<Int> = flow { emit(1) }
.shareIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
replay = 1
)
StateFlow / SharedFlow → Flow
kotlin
// StateFlow 和 SharedFlow 本身就是 Flow
val stateFlow: StateFlow<Int> = MutableStateFlow(0)
val flow: Flow<Int> = stateFlow // 自动转换
Compose State → Flow
kotlin
@Composable
fun MyScreen() {
var text by remember { mutableStateOf("") }
// 方法1: snapshotFlow
LaunchedEffect(Unit) {
snapshotFlow { text }
.debounce(300)
.collect { /* ... */ }
}
// 方法2: derivedStateOf + snapshotFlow
val textLength by remember {
derivedStateOf { text.length }
}
}
Flow → Compose State
kotlin
@Composable
fun MyScreen(viewModel: MyViewModel) {
// 方法1: collectAsState
val state by viewModel.stateFlow.collectAsState()
// 方法2: collectAsStateWithLifecycle(推荐)
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
// 方法3: produceState
val state by produceState(initialValue = 0) {
viewModel.flow.collect { value = it }
}
}
SharingStarted 策略详解
WhileSubscribed - 有订阅者时活跃(⭐ 最常用)
kotlin
SharingStarted.WhileSubscribed(
stopTimeoutMillis = 5000, // 最后一个订阅者离开后等待 5 秒
replayExpirationMillis = 0 // 重放过期时间
)
场景: UI 状态管理,节省资源
Eagerly - 立即开始
kotlin
SharingStarted.Eagerly
场景: 需要预加载的数据
Lazily - 第一个订阅者到来时开始
kotlin
SharingStarted.Lazily
场景: 延迟初始化,但希望保留值
实战最佳实践
1. ViewModel 中的状态管理
kotlin
@HiltViewModel
class MyViewModel @Inject constructor(
private val repository: MyRepository
) : ViewModel() {
// ✅ 使用 StateFlow 管理 UI 状态
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
// ✅ 使用 SharedFlow 管理一次性事件
private val _events = MutableSharedFlow<UiEvent>()
val events: SharedFlow<UiEvent> = _events.asSharedFlow()
// ✅ 使用 stateIn 转换 Flow
val dataList: StateFlow<List<Data>> = repository.getDataFlow()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = emptyList()
)
fun loadData() {
viewModelScope.launch {
repository.fetchData()
.catch { e ->
_events.emit(UiEvent.ShowError(e.message))
}
.collect { data ->
_uiState.update { it.copy(data = data) }
}
}
}
}
2. Repository 中的数据流
kotlin
class MyRepository @Inject constructor(
private val apiService: ApiService,
private val database: MyDatabase
) {
// ✅ 单一数据源
fun getDataFlow(): Flow<List<Data>> = database.dataDao()
.getAllFlow()
.map { entities -> entities.map { it.toDomain() } }
// ✅ 网络请求 + 本地缓存
suspend fun refreshData(): Result<Unit> = try {
val response = apiService.getData()
database.dataDao().insertAll(response.toEntities())
Result.success(Unit)
} catch (e: Exception) {
Result.failure(e)
}
// ✅ Flow 封装网络请求
fun getDataWithRefresh(): Flow<Resource<List<Data>>> = flow {
emit(Resource.Loading)
// 先发射缓存数据
val cachedData = database.dataDao().getAll()
if (cachedData.isNotEmpty()) {
emit(Resource.Success(cachedData.map { it.toDomain() }))
}
// 再请求网络
try {
val response = apiService.getData()
database.dataDao().insertAll(response.toEntities())
emit(Resource.Success(response.map { it.toDomain() }))
} catch (e: Exception) {
emit(Resource.Error(e))
}
}.flowOn(Dispatchers.IO)
}
3. 搜索功能完整实现
kotlin
@HiltViewModel
class SearchViewModel @Inject constructor(
private val repository: SearchRepository
) : ViewModel() {
private val _searchQuery = MutableStateFlow("")
val searchQuery: StateFlow<String> = _searchQuery.asStateFlow()
val searchResults: StateFlow<List<SearchResult>> = searchQuery
.debounce(300) // 防抖
.distinctUntilChanged() // 去重
.filter { it.length >= 2 } // 最少2个字符
.flatMapLatest { query -> // 取消之前的搜索
repository.search(query)
.catch { emit(emptyList()) } // 错误时返回空列表
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = emptyList()
)
fun updateSearchQuery(query: String) {
_searchQuery.value = query
}
}
// 在 Compose 中使用
@Composable
fun SearchScreen(viewModel: SearchViewModel = hiltViewModel()) {
val query by viewModel.searchQuery.collectAsStateWithLifecycle()
val results by viewModel.searchResults.collectAsStateWithLifecycle()
Column {
TextField(
value = query,
onValueChange = { viewModel.updateSearchQuery(it) },
placeholder = { Text("Search...") }
)
LazyColumn {
items(results) { result ->
SearchResultItem(result)
}
}
}
}
4. 多数据源组合
kotlin
@HiltViewModel
class DashboardViewModel @Inject constructor(
userRepository: UserRepository,
settingsRepository: SettingsRepository,
notificationsRepository: NotificationsRepository
) : ViewModel() {
// ✅ 组合多个数据源
val dashboardState: StateFlow<DashboardState> = combine(
userRepository.getUserFlow(),
settingsRepository.getSettingsFlow(),
notificationsRepository.getNotificationsFlow()
) { user, settings, notifications ->
DashboardState(
user = user,
settings = settings,
unreadCount = notifications.count { !it.isRead },
notifications = notifications
)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = DashboardState()
)
}
5. 分页加载
kotlin
@HiltViewModel
class ListViewModel @Inject constructor(
private val repository: ListRepository
) : ViewModel() {
private val _page = MutableStateFlow(0)
private val _items = MutableStateFlow<List<Item>>(emptyList())
val items: StateFlow<List<Item>> = _items.asStateFlow()
private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()
init {
viewModelScope.launch {
_page
.flatMapLatest { page ->
flow {
_isLoading.value = true
emit(repository.getItems(page))
}.onCompletion {
_isLoading.value = false
}
}
.catch { /* 处理错误 */ }
.collect { newItems ->
_items.update { it + newItems }
}
}
}
fun loadNextPage() {
if (!_isLoading.value) {
_page.value++
}
}
}
性能优化技巧
1. 使用 conflate 跳过中间值
kotlin
// 当生产者快,消费者慢时
dataFlow
.conflate() // 跳过中间值,只处理最新的
.collect { /* 处理 */ }
2. 使用 buffer 提高并发
kotlin
flow {
repeat(10) {
delay(100)
emit(it)
}
}
.buffer() // 生产者和消费者并发执行
.collect {
delay(300)
println(it)
}
3. 使用 distinctUntilChanged 避免重复计算
kotlin
stateFlow
.distinctUntilChanged() // 值相同时不触发
.collect { /* 更新 UI */ }
4. 选择合适的 SharingStarted 策略
kotlin
// ✅ 推荐:UI 不可见时停止收集
SharingStarted.WhileSubscribed(5000)
// ⚠️ 注意:一直活跃,消耗资源
SharingStarted.Eagerly
常见错误和解决方案
❌ 错误1:在 collect 中直接更新 UI
kotlin
// ❌ 错误
viewModelScope.launch {
flow.collect { value ->
// 不要在这里直接更新 UI State
_uiState.value = value
}
}
// ✅ 正确:使用 stateIn
val uiState = flow
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue)
❌ 错误2:忘记使用 flowOn
kotlin
// ❌ 可能阻塞主线程
flow {
val data = loadDataFromNetwork() // 网络请求
emit(data)
}.collect { /* UI 更新 */ }
// ✅ 正确
flow {
val data = loadDataFromNetwork()
emit(data)
}
.flowOn(Dispatchers.IO) // 在 IO 线程执行
.collect { /* UI 更新 */ }
❌ 错误3:在 Compose 中不使用 collectAsStateWithLifecycle
kotlin
// ❌ 后台仍在收集
val state by viewModel.stateFlow.collectAsState()
// ✅ 后台自动停止
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
❌ 错误4:SharedFlow 用于状态管理
kotlin
// ❌ 不适合状态管理
private val _uiState = MutableSharedFlow<UiState>()
// ✅ 使用 StateFlow
private val _uiState = MutableStateFlow(UiState())
调试技巧
1. 打印流的值
kotlin
flow
.onEach { println("Value: $it") }
.collect()
2. 监控流的生命周期
kotlin
flow
.onStart { println("Flow started") }
.onCompletion { println("Flow completed") }
.onEach { println("Value: $it") }
.collect()
3. 捕获并打印异常
kotlin
flow
.catch { e ->
println("Error: ${e.message}")
e.printStackTrace()
}
.collect()
总结
Flow 操作符选择指南
| 需求 | 使用的操作符 |
|---|---|
| 简单值转换 | map |
| 流的转换 | flatMapLatest |
| 过滤值 | filter |
| 防抖搜索 | debounce |
| 组合多个流 | combine |
| 异常处理 | catch |
| 切换线程 | flowOn |
| 转为状态 | stateIn |
StateFlow vs SharedFlow 选择
- ✅ StateFlow:UI 状态、配置、用户信息
- ✅ SharedFlow:一次性事件、导航、Toast
Compose 中收集 Flow
- ✅ 推荐 :
collectAsStateWithLifecycle() - ⚠️ 备选 :
collectAsState()(简单场景) - ⚠️ 事件 :
LaunchedEffect+collect
编写日期: 2025-01-19
版本: 1.0
适用于: Kotlin 1.9+, Compose 1.5+