深入理解 Flow 的终端操作符

我们来系统学习一下 Flow 的终端操作符。

Flow 是冷流,只有在调用了终端操作符之后,才会启动流的收集流程(collect),上游的代码才会开始执行。

获取单个元素

这类操作符用于从流中获取特定的值。

注意:它们都是挂起函数。

first() 和 firstOrNull()

first():它会等待并返回 Flow 发射的第一个 元素,然后立即取消流的收集。

kotlin 复制代码
fun main(): Unit = runBlocking {
    // 获取第一个值
    val flow = flowOf(1, 2, 3)
    println("First value: ${flow.first()}") // 输出: 1

    // 异常:流为空
    val emptyFlow = flowOf<Int>()
    try {
        emptyFlow.first()
    } catch (e: NoSuchElementException) {
        println("Error: ${e.message}") // 输出: Expected at least one element
    }
}

它的变体是 first{ predicate },会返回第一个满足条件的元素。

kotlin 复制代码
val flow = flowOf(1, 2, 3)
// 查找第一个大于等于 2 的元素
val value = flow.first { it >= 2 }

firstOrNull() 的行为和 first() 类似,区别在于当 Flow 为空时,它不会抛出异常,而是会返回 null。

使用场景

对于不需要持续监听变化的 Flow,我们会经常使用此操作符。

例如:读取 DataStore 的当前快照、将一次性的网络请求封装为 Flow。

读取完毕后,流就会自动关闭,不会造成资源泄漏。

last() 和 lastOrNull()

这两个操作符必须等待流完全结束后,才会返回流的最后一个元素。

千万注意 :如果在无限流 上调用 last(),会导致协程永远挂起,进而引出内存泄漏或界面无响应。

无限流场景:

  • SharedFlow
  • 通过 interval 创建的时间流
  • Room 数据库的查询 Flow,只要数据表变动,它就会发射新数据,所以它永远不会主动结束。因此,不要在 Dao 返回的 Flow 上调用 last()。

single() 与 singleOrNull()

这类操作符不仅仅是获取值,还有检验的性质。

single():期待流仅仅发射一个元素。

  • 如果流只发射了一个元素,会返回该元素。
  • 如果流为空,会抛出 NoSuchElementException。
  • 如果流的发射了多于一个元素,会抛出 IllegalStateException。

singleOrNull():同上,区别在于当流为空或是元素多于一个时,会返回 null。

注意:使用 singleOrNull(),你将无法区分"没有数据"或是"数据超出一个"的情况。如果要区分数据类型,应该使用 single()。

集合转换与统计

这类操作符会收集流中的所有数据,需要等待流的结束。所以它也同样存在无限挂起的风险。

count()

作用:获取流中元素的总个数。

变体:count { predicate } 会统计满足条件的元素个数。

toList(), toSet(), toCollection()

  • toList():将所有元素收集到 List 中。
  • toSet():将所有元素收集到 Set 中。
  • toCollection(destination):将所有元素添加到指定的集合实例中。

注意:Flow 的优势是流式处理,使用这些操作符会将所有数据一次性加载到内存中。如果数据量很大,极易导致 OOM。

流与通道的桥梁

produceIn(scope) 并不是一个挂起函数,而是一个普通函数。

它可以将一个 Flow(冷流)转为 ReceiveChannel(热通道)。

原理:其实在内部它只是利用传入的 CoroutineScope 启动一个新的协程来收集这个 Flow,并将数据发送到 Channel 中。

在 MVVM 架构中,将冷流转为热流暴露给 UI,我们通常会使用 stateIn 或 shareIn。但在需要利用 Channel 特有的并发能力时,produceIn 还是首选的工具。

相关推荐
2501_9159090624 分钟前
iOS应用从开发到上架App Store的完整发布流程与步骤指南
android·ios·小程序·https·uni-app·webview
狗凯之家源码网1 小时前
AI 步数修改提交系统源码应用场景与落地指南
android·数据库·人工智能·运动步数
ZHOUPUYU1 小时前
PHP 9.0 前瞻:下一代 PHP 会带来哪些新功能?
android·开发语言·php
JMchen2 小时前
性能优化——让Native代码飞起来
android·c++·性能优化
ZHOUPUYU2 小时前
PHP 8.2 的核心高级特性,包括只读类、类型系统增强
android·性能优化·php
ZHOUPUYU2 小时前
PHP 8.0 高级技术实战:从新特性到性能优化
android·性能优化·php
不爱说话郭德纲2 小时前
从“点点点”到一键出包:我把 uni-app x Android 离线打包做成了脚本
android·前端·uni-app
事圆则缓3 小时前
Android 崩溃优化与应用稳定性全解析:Crash、ANR、OOM 到线上治理
android
ZHOUPUYU3 小时前
PHP 高级技术实战:从原理到高并发架构落地
android·性能优化·php
stevenzqzq4 小时前
mvi的理解
android·compose