2. Flow 基础知识:创建 Flow 的三种姿势
说起 Flow,我刚开始学协程时最头疼的就是怎么把它“造”出来。其实说白了,Kotlin 官方给了我们三把钥匙:flow { }、flowOf()、.asFlow()。我个人习惯把它们看作手工作坊、预制菜和自动转化机——各有各的适用场景。
2.1 flow { } —— 最灵活的手工搭建方式
flow { } 是最基础的构建器。你在花括号内部写逻辑,用 emit() 把数据一个个发出去。我当初写第一个 Flow 时就踩了一个坑:忘了 suspend 函数不能在协程外调用——emit 本身是挂起函数,必须在协程作用域里使用。
基本用法:
val myFlow: Flow<Int> = flow {
for (i in 1..3) {
delay(100) // 模拟耗时操作
emit(i) // 发射数据
}
}
你想想看,这个 flow { } 背后 = 一个冷数据流。什么叫冷?就是没人 collect 它就不干活。这叫“惰性发射”,跟 RxJava 的 Observable 有点像,但更轻量。
💡 个人经验: 如果要发射的数据需要从网络或本地数据库获取,我强烈推荐用 flow { }。因为你可以写 emit() 的同时加各种错误处理和取消逻辑。我曾经用 flow 配合 retry 操作符实现了网络请求自动重试,代码比传统回调优雅十倍。
2.2 flowOf() —— 最简洁的固定值工厂
当你手头就是一堆现成数据,不想写 emit 循环?直接用 flowOf()。它接受可变参数,内部帮你逐个 emit。
val numbers: Flow<Int> = flowOf(1, 2, 3, 4, 5)
说白了,这就是个语法糖。我一般在测试或 mock 数据时最喜欢用它——写单元测试时,一行代码就能构建一个包含预定值的 Flow,比 mock 框架还顺手。
⚠️ 避坑指南: 有一次我在生产代码里用 flowOf(listOf(...)),结果每次都只发射一个 List 对象,而不是列表里的每个元素。后来才意识到:flowOf() 是把每个参数作为一个独立元素发射,如果传一个集合进去,那发射的就是整个集合本身。要小心!
2.3 asFlow() —— 从现有集合/序列转化
你手头已经有 List、Set 或 Sequence 了,不想重复造轮子?调一下 .asFlow() 即可。这是 Kotlin 协程库提供的扩展函数,把集合里每个元素依次发射出去。
| 源类型 | 转换代码 | 结果 Flow 元素类型 |
|---|---|---|
List<String> |
list.asFlow() |
String |
Set<Int> |
set.asFlow() |
Int |
Sequence<Double> |
seq.asFlow() |
Double |
示例:
val names = listOf("Alice", "Bob", "Charlie")
val nameFlow: Flow<String> = names.asFlow()
// 之后用 nameFlow.collect { println(it) } 即可
嗯,这里要注意:asFlow() 默认是逐个同步发射的。如果集合里有大量数据,它不会像 flow { } 那样给你加 delay 的机会——发射事件本身就是瞬时完成的。所以如果你需要控制发射间隔,还是老老实实用 flow { } 吧。
2.4 三种方式怎么选?一张表看明白
| 对比维度 | flow { } | flowOf() | asFlow() |
|---|---|---|---|
| 使用场景 | 自定义发射逻辑、异步数据源 | 固定列表、测试、简单数据 | 已有集合/序列,直接转化 |
| 灵活性 | ★★★★★ | ★★☆☆☆ | ★★★☆☆ |
| 代码简洁性 | ★★☆☆☆ | ★★★★★ | ★★★★☆ |
| 支持挂起函数 | 是(可任意调用) | 否(仅发射) | 否(同步转换) |
2.5 我记的几个小经验
- 调试时多用 flowOf —— 我习惯在测试里用
flowOf(1, 2, 3)快速验证操作符行为,比写flow { }少两行大括号。 - asFlow 和 flowOn 搭配有坑 —— 如果你用
list.asFlow()然后在后面调了flowOn,记住:asFlow 转换本身是在调用线程执行的,flowOn 只影响下游操作符的上下文。想控制发射线程?还是用flow { }吧。 - 小心空指针 —— flow Of 不会自动做空安全检查,但 Kotlin 类型系统会帮你。如果传了一个 null 进去,Flow 元素类型就会变成
Type?,collect 时要注意判空。
总结一下: 创建 Flow 就这三种姿势。没有谁好,选合适的就行。我个人最喜欢 flow { },因为它给我最大的控制感;但要是赶工期写 demo,我肯定选 flowOf 或 asFlow——少写代码永远是对自己好。