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