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

相关推荐
xiaoshiquan12065 小时前
Gradle 复合构建:宿主项目直接依赖 SDK 源码模块的配置实践
android
小师兄吃牛肉8 小时前
Android Studio 2025 专业介绍:新特性、性能与开发体验全面解析
android·ide·android studio
美摄科技8 小时前
安卓离线类美颜特效SDK
android
fengci.9 小时前
BUUCTF(困难部分)
android·开发语言·前端·学习·php
传奇开心果11 小时前
【Compose Multiplatform跨端声明式UI实用技术】第9课:跨平台 ViewModel 与状态架构
android·kotlin
用户693717500138412 小时前
当你向领导申请 AI 编程工具预算时,如果领导追问“能提效多少,有量化数据吗”,你该怎么回答?
android·后端·ios
hunterandroid13 小时前
WorkManager 后台任务调度实战:从一次性任务到周期执行
android
事圆则缓13 小时前
Android 屏幕适配的前世今生
android
andyweike13 小时前
kotlin dsl library工程怎么配置
android·开发语言·kotlin
啵啵啵鱼13 小时前
九、JDBC 操作 MySQL 数据库
android·数据库·mysql