Kotlin Flow 的操作符非常丰富,它们是处理异步数据流的核心武器。Flow 的操作符可以像流水线上的工人一样,对数据进行过滤、加工、组合或线程切换。
为了方便记忆和学习,我们可以将 Flow 的常用操作符分为四大类:中间操作符 、终端操作符 、组合/展平操作符 以及 线程与异常处理操作符。
1. 中间操作符 (Intermediate Operators)
这类操作符的作用是修改数据流,但它们是惰性的(Lazy) 。也就是说,调用它们时并不会让 Flow 跑起来,它们只是定制了数据加工的规则。
| 操作符 | 作用 | 代码示例 |
|---|---|---|
map |
将流中的每个元素转换成另一种形式。 | flow.map { "数字: $it" } |
filter |
过滤数据,只有符合条件的元素才能留下来。 | flow.filter { it % 2 == 0 } |
take |
只取前 N 个元素,之后自动取消流。 | flow.take(2) |
drop |
跳过前 N 个元素,从第 N+1 个开始收集。 | flow.drop(2) |
onEach |
在每个元素发射时触发一个副作用(如打印日志),但不改变元素本身。 | flow.onEach { println(it) } |
2. 终端操作符 (Terminal Operators)
终端操作符是真正触发 Flow 开始工作 的开关。没有终端操作符,Flow 就是一滩死水。终端操作符都是挂起函数(suspend) 。
collectcollect:最基础的终端操作符,开始收集数据并处理。first() / firstOrNull()first() / firstOrNull() :只等待并获取流中的第一个元素,随后立刻终止流。toList() / toSet()toList() / toSet() :将流中的所有元素收集并转化为一个 Kotlin 集合。reduce() / fold()reduce() / fold() :将流中的元素进行累加组合。例如计算 1 到 5 的总和。
3. 组合与展平操作符 (Combining & Flattening)
在 Android 实战中,我们经常需要处理多个数据流的交织情况(例如:同时等待网络请求 A 和 B 的结果,或者根据用户输入的关键词动态切换请求)。
组合:zip vs combine
zipzip(严格对齐): 必须等两个流都发射了相同索引的元素,才会融合成一对。如果流 A 发了 3 个,流 B 发了 5 个,那结果只有 3 个。combinecombine(最新组合): 任何一个流发射了新数据,都会拿它与另一个流的最新产生的元素融合成新结果(Android 中最常用,用于合并多个 UI 状态)。
展平:flatMapLatest (最常用于搜索框输入)
当一个旧的流还在处理时,新数据来了,flatMapLatest 会立刻取消掉旧流的后续处理,转而处理新流。
rust
// 模拟搜索框输入监听
searchQueryFlow
.debounce(300) // 防抖 300 毫秒
.flatMapLatest { query ->
// 如果用户连续输入,旧的网络请求会被自动 cancel 掉,只执行最新的搜索
repository.searchInDatabase(query)
}
.collect { results -> /* 刷新界面 */ }
4. 线程、生命周期与异常处理
在 Android 中,数据流的控制离不开线程和异常捕获,官方为此提供了非常优雅的操作符:
💡 核心操作符:flowOn (改变上游线程)
在 Flow 中,默认情况下,消费者在什么线程 collect,生产者就在什么线程发射。 如果你想让生产者去子线程读数据库,UI 层在主线程刷新,就要用 flowOn:
scss
flow {
emit(readHeavyDatabase()) // 这段代码运行在 Dispatchers.IO
}
.flowOn(Dispatchers.IO) // ✨ 改变它【上游】代码的执行线程
.collect { data ->
updateUI(data) // 这段代码运行在主线程(由 collect 所在的生命周期决定)
}
异常捕获:catch
不要在 Flow 内部用传统的 try-catch 包裹 emit(),官方推荐使用 catch 操作符,它能捕获上游发生的任何异常:
scss
viewModel.uiState
.catch { exception ->
emit(UiState.Error(exception.message)) // 捕获上游错误,并安全地发射一个错误状态
}
.collect { ... }
生命周期监听:onStart 与 onCompletion
onStartonStart :在流正式开始发射前执行,非常适合用来显示加载圈 (Loading) 。onCompletiononCompletion :在流结束(无论是正常结束还是发生异常)时执行,适合用来隐藏加载圈。
在这些操作符中,combinecombine 和 flatMapLatestflatMapLatest 在 Android 实际业务(如多条件筛选列表、搜索框联动)中用得最频繁。您想看一个具体的**"多条件筛选(比如:同时筛选商品分类和价格范围)"**的 combine 实战演练吗?