深入理解 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,我们通常会使用 stateInshareIn。但在需要利用 Channel 特有的并发能力时,produceIn 还是首选的工具。

相关推荐
杜子不疼.2 小时前
【C++】深入解析AVL树:平衡搜索树的核心概念与实现
android·c++·算法
介一安全3 小时前
【Frida Android】实战篇7:SSL Pinning 证书绑定绕过 Hook 教程阶段总结
android·网络安全·逆向·安全性测试·frida
用户2018792831674 小时前
Android 混淆引发的反序列化问题浅析
android
00后程序员张4 小时前
iOS 性能优化的体系化方法论 从启动速度到渲染链路的多工具协同优化
android·ios·性能优化·小程序·uni-app·iphone·webview
游戏开发爱好者85 小时前
iPhone重启日志深度解析与故障代码诊断
android·ios·小程序·https·uni-app·iphone·webview
TDengine (老段)7 小时前
TDengine 字符串函数 TO_BASE64 用户手册
android·大数据·服务器·物联网·时序数据库·tdengine·涛思数据
spencer_tseng8 小时前
Eclipse Oxygen 4.7.2 ADT(android developer tools) Plugin
android·java·eclipse
来来走走9 小时前
Android开发(Kotlin) 协程
android·java·kotlin