只需要先记住这几类核心操作符:
kotlin
创建 Flow
↓
转换数据
↓
过滤数据
↓
合并数据
↓
控制线程/异常/生命周期
↓
终结收集
一、创建 Flow:数据从哪里来
这类操作符负责 创建 Flow。
| 操作符 | 作用 |
|---|---|
flow {} |
最基础的 Flow 构建器 |
flowOf() |
直接发出固定几个值 |
asFlow() |
把集合、数组、序列转成 Flow |
callbackFlow {} |
把回调 API 转成 Flow |
channelFlow {} |
允许多协程发送数据 |
常用记忆
kotlin
flow {
emit(1)
emit(2)
}
kotlin
flowOf(1, 2, 3)
kotlin
listOf(1, 2, 3).asFlow()
最常用的是:
kotlin
flow {}
flowOf()
asFlow()
callbackFlow {}
二、转换操作符:把数据变成另一种数据
这类就是 对每个元素做加工。
| 操作符 | 作用 |
|---|---|
map |
一进一出 |
transform |
一进多出/零出/多出 |
mapLatest |
新元素来了,取消旧元素的转换 |
onEach |
对每个元素做额外动作,一般不改变值 |
1. map
kotlin
flowOf(1, 2, 3)
.map { it * 2 }
.collect {
println(it)
}
输出: 2 4 6
记住:
kotlin
map:一个元素转成另一个元素
2. transform
scss
flowOf(1, 2, 3)
.transform {
emit("开始 $it")
emit("结束 $it")
}
.collect {
println(it)
}
输出:
kotlin
开始 1
结束 1
开始 2
结束 2
开始 3
结束 3
记住:
kotlin
map:只能返回一个
transform:可以 emit 多个
3. onEach
kotlin
flowOf(1, 2, 3)
.onEach {
println("准备发送 $it")
}
.collect {
println("收到 $it")
}
onEach 通常用于:
kotlin
打印日志
更新状态
做副作用
加 delay
三、过滤操作符:哪些数据要,哪些不要
| 操作符 | 作用 |
|---|---|
filter |
保留满足条件的元素 |
filterNot |
过滤掉满足条件的元素 |
take |
只取前几个 |
drop |
跳过前几个 |
distinctUntilChanged |
相邻重复值只保留一个 |
1. filter
kotlin
flowOf(1, 2, 3, 4)
.filter { it % 2 == 0 }
.collect {
println(it)
}
输出:2 4
2. take
kotlin
flowOf(1, 2, 3, 4)
.take(2)
.collect {
println(it)
}
输出: 1 2
take(2) 收到两个元素后,会取消上游。
3. distinctUntilChanged
kotlin
flowOf(1, 1, 2, 2, 1)
.distinctUntilChanged()
.collect {
println(it)
}
输出: 1 2 1
注意它只去掉 相邻重复,不是全局去重。
四、合并操作符:多个 Flow 怎么合成一个
| 操作符 | 记忆方式 |
|---|---|
merge |
多个事件流混在一起,谁先来发谁 |
combine |
多个状态流取最新值组合 |
zip |
两个流一一配对 |
flattenConcat |
Flow<Flow<T>> 串行展开 |
flattenMerge |
Flow<Flow<T>> 并发展开 |
flatMapConcat |
map + flattenConcat |
flatMapMerge |
map + flattenMerge |
flatMapLatest |
新 Flow 来了,取消旧 Flow |
combineTransform |
combine 后可以自己 emit 多个值 |
最重要的 3 个:merge、combine、zip
merge
kotlin
多个 Flow 混合,谁先发谁先到
kotlinkotlin
merge(flow1, flow2)
适合:
kotlin
多个事件源混合
多个不相关数据源同时监听
combine
kotlin
多个 Flow 都有值后,任意一个更新,就拿最新值重新组合
kotlin
flow1.combine(flow2) { a, b ->
"$a-$b"
}
适合:
kotlin
UIState 组合
多个状态源组合
表单状态组合
zip
kotlin
第一个配第一个,第二个配第二个
css
flow1.zip(flow2) { a, b ->
"$a-$b"
}
适合:
kotlin
一一配对
两个数据源按位置对应
最重要的 3 个 flatMap
| 操作符 | 作用 |
|---|---|
flatMapConcat |
一个一个执行,保证顺序 |
flatMapMerge |
并发执行,不保证整体顺序 |
flatMapLatest |
只保留最新,取消旧任务 |
记忆:
kotlin
Concat:排队
Merge:并发
Latest:最新
五、线程和上下文操作符
| 操作符 | 作用 |
|---|---|
flowOn |
切换上游执行线程 |
buffer |
上游和下游之间加缓冲 |
conflate |
下游慢时,只保留最新值 |
debounce |
防抖,一段时间内只取最后一个 |
sample |
采样,固定时间取一个最新值 |
1. flowOn
kotlin
flow {
emit(loadData())
}
.flowOn(Dispatchers.IO)
.collect {
render(it)
}
记住:
kotlin
flowOn 影响的是它上游的执行线程
也就是:
scss
flow {
emit(loadData())
}
.flowOn(Dispatchers.IO)
这里 flow {} 在 IO 线程执行。
2. buffer
默认 Flow 是上下游串行背压的。
kotlin
flow
.buffer()
.collect {
delay(1000)
println(it)
}
buffer() 可以让上游提前生产,下游慢慢消费。
3. conflate
kotlin
flow
.conflate()
.collect {
delay(1000)
println(it)
}
下游处理慢时,中间旧值可能被跳过,只处理最新值。
适合:
kotlin
进度条
位置更新
传感器数据
4. debounce
kotlin
queryFlow
.debounce(300)
.flatMapLatest {
search(it)
}
适合搜索框:
kotlin
用户停止输入 300ms 后,再发起搜索
六、异常处理操作符
| 操作符 | 作用 |
|---|---|
catch |
捕获上游异常 |
retry |
失败后重试 |
retryWhen |
自定义重试条件 |
onCompletion |
Flow 完成时回调,成功失败都会走 |
1. catch
kotlin
flow {
emit(1)
throw RuntimeException("error")
}
.catch { e ->
emit(-1)
}
.collect {
println(it)
}
输出: 1 -1
记住:
kotlin
catch 捕获的是它上游的异常
2. onCompletion
kotlin
flowOf(1, 2, 3)
.onCompletion {
println("Flow 结束了")
}
.collect {
println(it)
}
输出:
css
1
2
3
Flow 结束了
七、生命周期/启动前后操作符
| 操作符 | 作用 |
|---|---|
onStart |
收集开始前执行 |
onEach |
每个元素发给下游前执行 |
onCompletion |
收集结束时执行 |
launchIn |
在指定 scope 中启动收集 |
常见链路
kotlin
flow
.onStart {
emit(Loading)
}
.catch {
emit(Error)
}
.onEach {
render(it)
}
.launchIn(viewModelScope)
这在 Android 里很常见。
八、终结性操作符
终结性操作符负责 真正开始收集 Flow。
| 操作符 | 作用 |
|---|---|
collect |
收集每个元素 |
collectLatest |
新元素来了,取消旧元素处理 |
first |
取第一个 |
firstOrNull |
取第一个,没有返回 null |
last |
取最后一个 |
single |
只有一个元素才返回 |
count |
统计数量 |
toList |
收集成 List |
toSet |
收集成 Set |
reduce |
无初始值聚合 |
fold |
有初始值聚合 |
launchIn |
启动异步收集,返回 Job |
produceIn |
转成 Channel |
九、真正需要优先记住的 20 个
不用一口气记所有,先记这 20 个就够用了。
第一层:必须熟
| 类别 | 操作符 |
|---|---|
| 创建 | flow {}、flowOf()、asFlow() |
| 转换 | map、transform、onEach |
| 过滤 | filter、take、distinctUntilChanged |
| 合并 | merge、combine、zip |
| flatMap | flatMapConcat、flatMapMerge、flatMapLatest |
| 异常 | catch、onCompletion |
| 线程 | flowOn |
| 终结 | collect、first、toList |
第二层:知道什么时候查
| 场景 | 操作符 |
|---|---|
| 搜索框防抖 | debounce |
| 下游慢,只要最新值 | conflate |
| 上下游解耦 | buffer |
| 失败重试 | retry、retryWhen |
| 只处理最新收集逻辑 | collectLatest |
| 转成 Channel | produceIn |
| Android 中启动收集 | launchIn |
十、最简单的记忆口诀
可以按这个背:
kotlin
创建:flow、flowOf、asFlow
转换:map、transform、onEach
过滤:filter、take、distinct
合并:merge、combine、zip
展开:concat、merge、latest
线程:flowOn、buffer、conflate
异常:catch、retry、completion
终结:collect、first、list、count、reduce、fold
十一、Android 中最常用的一套组合
实际项目里最常见的是这种:
kotlin
repository.getDataFlow()
.onStart {
emit(UiState.Loading)
}
.map { data ->
UiState.Success(data)
}
.catch { e ->
emit(UiState.Error(e.message ?: "unknown error"))
}
.flowOn(Dispatchers.IO)
.collect { state ->
render(state)
}
搜索框常见:
kotlin
queryFlow
.debounce(300)
.distinctUntilChanged()
.flatMapLatest { query ->
searchRepository.search(query)
}
.catch {
emit(emptyList())
}
.collect {
showSearchResult(it)
}
多个状态组合常见:
kotlin
combine(userFlow, messageFlow, networkFlow) { user, message, network ->
UiState(user, message, network)
}.collect {
render(it)
}
最后总结
不用记所有 Flow 操作符。
优先记这几个核心:
kotlin
map:转换
filter:过滤
onEach:副作用
flowOn:切线程
catch:异常
collect:收集
merge:混合多个流
combine:组合最新状态
zip:一一配对
flatMapConcat:顺序任务
flatMapMerge:并发任务
flatMapLatest:最新任务
把这几个吃透,Flow 的大部分业务场景就够用了。