Kotlin常用的Flow 操作符整理

只需要先记住这几类核心操作符:

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 个:mergecombinezip

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()
转换 maptransformonEach
过滤 filtertakedistinctUntilChanged
合并 mergecombinezip
flatMap flatMapConcatflatMapMergeflatMapLatest
异常 catchonCompletion
线程 flowOn
终结 collectfirsttoList

第二层:知道什么时候查

场景 操作符
搜索框防抖 debounce
下游慢,只要最新值 conflate
上下游解耦 buffer
失败重试 retryretryWhen
只处理最新收集逻辑 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 的大部分业务场景就够用了。

相关推荐
IT_陈寒3 小时前
React的useState居然还有这种坑?我差点删库跑路
前端·人工智能·后端
Pedantic4 小时前
SwiftUI 手势笔记
前端·后端
橙子家4 小时前
浏览器缓存之【结构化数据库与缓存】: IndexedDB、Cache storage 和 Storage buckets
前端
user20585561518134 小时前
X6 中边悬浮置顶,规避 `mouseleave` 事件丢失问题
前端
李明卫杭州4 小时前
CSS aspect-ratio 属性完全指南
前端
Pedantic6 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘7 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆7 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl