2、Flow基础:Flow的创建、Flow的发射与收集、冷流与热流的概念

好,咱们正式开始聊 Flow。说实话,我第一次接触 Kotlin Flow 的时候,脑子里全是 RxJava 的影子。但用了一段时间后,我发现 Flow 更「Kotlin」——它跟协程是天生一对。今天这一章,我会带你从零开始,把 Flow 的创建、发射、收集,以及冷流和热流这两个核心概念彻底搞明白。

2.1 Flow 是什么?

简单说,Flow 就是一个异步数据流。它可以按顺序发射多个值,而不是像 suspend 函数那样只返回一个结果。你想想看,在 Android 开发里,我们经常需要监听数据库变化、网络状态、用户输入……这些场景都是「多个值随时间到达」,Flow 就是为这个而生的。

核心要点:Flow 是冷流,默认情况下它不干活,只有被收集时才开始发射数据。

2.2 创建 Flow

创建 Flow 最常用的方式就是使用 flow { } 构建器。我在项目中几乎每天都在用这个。

val myFlow: Flow<Int> = flow {
    // 发射数据
    emit(1)
    emit(2)
    emit(3)
}

嗯,这里要注意:emit() 是一个挂起函数,它只能在 Flow 的构建器内部调用。你可能会问,为什么不能直接返回一个 List?因为 Flow 是「懒」的,它支持背压,而且可以异步发射。

除了 flow { },还有几种常见的创建方式:

方式 说明 示例
flowOf 发射固定值 flowOf(1, 2, 3)
asFlow 集合/序列转 Flow listOf(1,2,3).asFlow()
callbackFlow 回调转 Flow 用于监听器、传感器等

我个人习惯用 flow { },因为它最灵活。比如在数据库查询时,我可以这样写:

fun getUserFlow(): Flow<User> = flow {
    val user = userDao.getUser() // 挂起函数
    emit(user)
}

2.3 发射与收集

发射用 emit(),收集用 collect()。这俩是 Flow 最核心的操作。

// 收集数据
myFlow.collect { value ->
    println("收到: $value")
}

收集是一个终端操作,它会挂起当前协程,直到 Flow 完成。我在项目中遇到过一个问题:如果在 Activity 里直接 collect,忘记取消,就会导致内存泄漏。所以一定要配合 lifecycleScope 或者 repeatOnLifecycle 来使用。

避坑指南:我曾经在 Fragment 里直接 collect 一个 Flow,结果页面销毁了还在发射,导致崩溃。后来我改用 viewLifecycleOwner.lifecycleScope.launch 配合 repeatOnLifecycle,问题就解决了。

Flow 的发射是顺序执行的。什么意思?就是 emit(1) 之后,必须等 collect 处理完 1,才会发射 2。这跟 RxJava 的背压有点像,但 Flow 更简洁。

2.4 冷流与热流

这是 Flow 里最容易混淆的概念。我刚开始学的时候也绕了很久。说白了:

  • 冷流:没有收集者就不干活。每个收集者都会从头开始接收数据。
  • 热流:不管有没有收集者,数据都在发射。新来的收集者只能收到之后的数据。

咱们用代码感受一下:

// 冷流示例
val coldFlow = flow {
    println("Flow 开始工作")
    emit("A")
    emit("B")
}

// 第一次收集
coldFlow.collect { println("收集者1: $it") }
// 第二次收集
coldFlow.collect { println("收集者2: $it") }

// 输出:
// Flow 开始工作
// 收集者1: A
// 收集者1: B
// Flow 开始工作  <-- 又执行了一次!
// 收集者2: A
// 收集者2: B

看到了吗?每次 collect 都会重新执行 flow 构建器。这就是冷流的典型特征。

那热流呢?Kotlin 提供了 StateFlowSharedFlow。它们是有状态的,数据在多个收集者之间共享。

// 热流示例 - SharedFlow
val sharedFlow = MutableSharedFlow<String>()

// 发射数据(不依赖收集者)
scope.launch {
    sharedFlow.emit("X")
    sharedFlow.emit("Y")
}

// 收集者只能收到之后的数据
scope.launch {
    sharedFlow.collect { println("收到: $it") }
}

我的经验:在 Android 里,StateFlow 最适合保存 UI 状态,比如 ViewModel 里的数据。SharedFlow 适合事件通知,比如一次性提示。我一般用 StateFlow 做状态管理,SharedFlow 做事件总线。

2.5 知识体系图

下面这张图帮你理清 Flow 的基础脉络:

Kotlin Flow 创建方式:flow { } / flowOf / asFlow / callbackFlow 核心操作:emit() 发射 → collect() 收集 冷流 (Cold Flow) 无收集者不工作 每个收集者从头开始 热流 (Hot Flow) 持续发射数据 新收集者只收之后数据

2.6 实际项目中的选择

说了这么多,到底什么时候用冷流,什么时候用热流?我总结了一个简单的原则:

  • 数据是一次性查询结果:用冷流。比如从数据库查一条记录,每次 collect 都重新查。
  • 数据是持续变化的:用热流。比如用户登录状态、位置更新、通知计数。
  • 需要多个观察者共享同一份数据:用热流。比如多个 Fragment 监听同一个 ViewModel 的状态。

记住:冷流是「按需生产」,热流是「广播通知」。选对类型,你的代码会干净很多。

好了,这一章的内容就到这里。Flow 的基础其实不复杂,关键是理解冷热流的区别。下一章我们会深入操作符,到时候你会发现 Flow 的真正威力。


公众号:蓝海资料掘金营,微信deep3321