创建Flow:三种方式,一个选择

创建Flow这件事,说起来很简单,但实际项目里选哪种方式,还是有点讲究的。我个人习惯把Flow的创建分为三类:全程手动控制的 flow builder、一把梭的 flowOf、以及无脑转换的 asFlow。

你想想看,我们写异步代码的时候,数据源可能是网络、数据库、或者用户输入。怎么把这些数据塞进Flow呢?下面我给你拆开揉碎了讲。

创建Flow的三种方式 创建Flow:三根支柱 Flow 创建方式 flow builder flowOf asFlow 扩展 emit 任意数量值 支持异常、挂起、 callback 转 Flow 固定值集合 仅发射给定元素 适合构造测试数据 集合/序列转换 List、Set、Sequence 一步到位转 Flow

1. flow builder:最自由的姿势

flow { ... } 这个构建器,是我用得最多的。它可以让你在协程环境下,手动发射(emit)任意数量的值。你需要的就是在 lambda 里调用 emit() 方法。

举个例子,假如你要监听一个数据库游标的变化,或者轮询一个网络状态,用 flow builder 就可以精确控制何时发数据、发多少数据。

val sensorFlow: Flow<Int> = flow {
    for (i in 1..5) {
        delay(100)          // 模拟传输延迟
        emit(i * 10)       // 发送一个数据
    }
}

这里有个细节,flow builder 是冷流。什么意思?就是没人在末端 collect,它根本就不跑。我在项目中遇到过有人把 flow builder 当热流用,结果一直不执行,查了半天才发现是没人 collect。

关键特性:
  • 可以发射任意数量元素
  • 内部支持挂起函数(比如 delay、网络请求)
  • 天然支持异常处理(try-catch 包裹 emit)
我的小技巧: 如果你想在 flow builder 里做复杂的业务流(比如先查缓存,再查网络,最后合并),直接写在一个 flow 里,比用多个 Flow 再组合要直观得多。

2. flowOf:极简主义者专用

说白了,flowOf 就是给你一个固定值的 Flow。你传几个参数进去,它就发射几个值。

val nameFlow: Flow<String> = flowOf("Kotlin", "Flow", "Async")

这段代码会依次发射 "Kotlin"、"Flow"、"Async" 三个字符串,然后自动结束。它本质上就是对 flow builder 的一个语法糖包装。

什么时候用? 我一般在单元测试里大量使用 flowOf。比如写一个方法返回 Flow,但在测试里我不想真的走网络,直接 flowOf(mockResult) 搞定。

另外还有一个场景,就是在静态配置或者常量数据里。比如 App 的版本号列表,或者一些枚举配置,用 flowOf 非常干净。

避坑: 我曾经在一次代码评审里发现有人用 flowOf 传了一个大 List,然后又在 collect 里遍历这个 List,完全没发挥 Flow 的异步优势。记住,flowOf 是固定有限序列,别拿它来做无限流。

3. asFlow:万物皆可 Flow

这个扩展函数可以让现有的集合、Sequence、甚至某些特定类型,快速转换成 Flow。最常用的就是 .asFlow() 放在 List 或者 Set 后面。

val intList = listOf(1, 2, 3, 4, 5)
val flowFromList: Flow<Int> = intList.asFlow()

这段代码会依次发射 1, 2, 3, 4, 5。效果与 flowOf 类似,但它更灵活——你可以基于已有的集合数据来创建 Flow。

我记得有一次在调试一个 ViewPager 的分页加载时,后台返回的是一个 List,我需要把它转成 Flow 然后做前端缓存合并。直接用 .asFlow() 就完成了,不用再写一个额外的 flow builder,代码量少了一半。

除了集合,Sequence 也可以用 asFlow:

val seq = sequence {
    yield("A"); yield("B"); yield("C")
}
val flowFromSeq = seq.asFlow()

三种方式对比

方式 适用场景 数据量 可否挂起
flow { ... } 动态、异步、可变数据 无限/有限 ✔ 是
flowOf(...) 固定值、测试数据、常量 有限 ✘ 否
.asFlow() 集合/序列转Flow、数据迁移 有限 ✘ 否(视来源)

选型原则:一句话总结

如果你要控制每个数据的发射时机,或者想在里面做挂起操作,就用 flow builder。其他情况,只要数据是现成的有限集合,flowOf 或者 asFlow 会更简洁。嗯,其实没有的对错,关键看你在哪个上下文中写代码。

个人习惯:
  • 单元测试里:我用 flowOf 构造预期结果,简单明了。
  • 网络库封装:一定用 flow builder,因为要处理 retry、timeout。
  • 数据库 DAO 返回 List:我会用 .asFlow() 快速过渡,如果后续需要加合并逻辑,再考虑改成 flow builder。

三种创建方式,说白了就是三条腿走路。你掌握了它们,后续学 Flow 的各种操作符(map、filter、flatMapMerge 等等)才能跑得稳。我记得有的初学者容易在这上面纠结,其实你随便选一种开始写,写着写着自然就知道该换哪种了。