Kotlin Flow操作符学习

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 实战演练吗?

相关推荐
2501_9327502639 分钟前
Android 自定义 View 入门
android
事圆则缓3 小时前
Android 开启线程的几种方式:Thread、线程池、HandlerThread 与协程
android
ZealSinger6 小时前
Kotlin后端别再用GlobalScope
kotlin·协程·后端开发·结构化并发
半摆烂日常17 小时前
低代码平台API对接实践:接口鉴权与数据同步的完整实现
android·低代码·rxjava
事圆则缓18 小时前
volatile 使用场景全解析:它能保证什么,又解决不了什么
android
蒲公英内测分发19 小时前
智能投影仪参加海外展会,未上架的 Android 配套 App 怎么让客户限时试用?
android
ii_best19 小时前
按键精灵手机端开发安卓版实战:手写一个可最小化、可拖动的「运行日志悬浮窗」(附完整 源码 + 踩坑记录)
android·ios·智能手机·自动化·ai编程·按键精灵
终端安全笔记19 小时前
安卓做 MDM 管控,先分清三种注册入口
android·安全·智能手机
乌萨达20 小时前
手机模拟器安卓怎么用?电脑安装应用、玩手游与账号同步指南
android·智能手机·电脑·雷电模拟器
美狐美颜SDK开放平台20 小时前
Android与iOS直播APP美颜有什么区别?视频美颜SDK开发详解
android·ios·音视频·视频美颜sdk