当前位置: 首页 > news >正文

Kotlin协程:Flow的异常处理

示例代码如下:

launch(Dispatchers.Main) {// 第一部分flow {emit(1)throw NullPointerException("e")}.catch {Log.d("liduo", "onCreate1: $it")}.collect {Log.d("liudo", "onCreate2: $it")}// 第二部分flow {emit(1)}.onCompletion {Log.d("liduo", "onCreate3: $it")}.collect {Log.d("liudo", "onCreate4: $it")}// 第三部分flow {emit(1)throw NullPointerException("e")}.retryWhen { cause, attempt ->cause !is NullPointerException && attempt <= 2}.collect {Log.d("liudo", "onCreate5: $it")}
}
复制代码

一.catch方法

catch方法用于捕获上游流产生的异常,代码如下:

publicfun<T> Flow<T>.catch(action: suspendFlowCollector<T>.(cause: Throwable) -> Unit): Flow<T> =flow { // 创建Flow对象// 触发上游流的执行,并捕获异常val exception = catchImpl(this)// 捕获到异常,则回调action处理if (exception != null) action(exception)}
复制代码

catch方法是Flow接口的扩展方法,并返回一个Flow类型的对象。在catch方法中,调用flow方法创建了一个Flow对象。

catch方法核心是通过catchImpl方法实现异常的捕获,如果成功捕获到异常,则回调参数action处理。这里参数action是FlowCollector接口的扩展方法,因此可以继续调用emit方法,向下游发送值。

1.catchImpl方法

当下游调用collect方法时,会触发catch方法创建的Flow对象的执行,并调用catchImpl方法来处理,代码如下:

internalsuspendfun<T> Flow<T>.catchImpl(collector: FlowCollector<T>
): Throwable? {// 保存下游流执行抛出的异常var fromDownstream: Throwable? = nulltry {// 触发上游流的执行collect {try {// 将上游流发送的值作为参数,触发下游流执行collector.emit(it)} catch (e: Throwable) { // 如果下游流在执行中发生异常,保存并抛出fromDownstream = ethrow e}}} catch (e: Throwable) { // 这里捕获的异常,可能为上游流的异常——collect方法,// 也可能为下游流的异常——emit方法// 如果异常是下游流产生的异常,或者是协程取消时抛出的异常if (e.isSameExceptionAs(fromDownstream) || e.isCancellationCause(coroutineContext)) {throw e // 再次抛出,交给下游处理} else { // 如果是上游流的异常且不为协程取消异常return e // 成功捕获}}// 未捕获到异常,返回returnnull
}
复制代码

catchImpl方法是Flow接口的扩展方法,因此在调用collect方法时,会触发上游流的执行。catchImpl方法的核心在于:将上游发出的值传递给下游处理,并对这一过程进行了异常捕获操作。

二. onCompletion方法

onCompletion方法用于在上游的流全部执行完毕后最后执行,代码如下:

publicfun<T> Flow<T>.onCompletion(action: suspendFlowCollector<T>.(cause: Throwable?) -> Unit
): Flow<T> = unsafeFlow { // 创建一个Flow对象try {// 触发上游流的执行// this表示下游的FlowCollectorcollect(this)} catch (e: Throwable) {// 如果下游发生异常// 将异常封装成ThrowingCollector类型的FlowCollector,并回调参数action,ThrowingCollector(e).invokeSafely(action, e)// 抛出异常throw e}// 如果正常执行结束,会走到这里val sc = SafeCollector(this, currentCoroutineContext())try {// 回调执行参数actionsc.action(null)} finally {sc.releaseIntercepted()}
}
复制代码

onCompletion方法是Flow接口的扩展方法,因此在调用collect方法时,会触发上游流的执行。同时,传入this作为参数,this表示下游流调用collect方法时,传给unsafeFlow方法创建的Flow对象的类型为FlowCollector的对象。onCompletion方法的核心在于:将自身创建的Flow对象作为上游与下游的连接容器,只有当流全部执行完毕或执行过程中发生异常,collect方法才可以执行完成,继续向下执行。

1.unsafeFlow方法

unsafeFlow方法用于创建一个类型为Flow对象,与之前在Kotlin协程:Flow基础原理提到过的SafeFlow类相比,unsafeFlow方法创建的Flow对象不会对执行的上下文进行检查,代码如下:

@PublishedApiinternalinlinefun<T>unsafeFlow(@BuilderInferencecrossinline block: suspendFlowCollector<T>.() -> Unit): Flow<T> {// 返回一个匿名内部类returnobject : Flow<T> {// 回调collect方法是直接执行blockoverridesuspendfuncollect(collector: FlowCollector<T>) {collector.block()}}
}
复制代码

虽然onCompletion方法内部使用unsafeFlow方法创建Flow对象,但却使用了SafeCollector类。根据之前在Kotlin协程:Flow基础原理提到的,调用SafeCollector类的emit方法时,会对上下文进行检查。因此实际效果与使用SafeFlow类效果相同。

2. ThrowingCollector类

ThrowingCollector类也是一种FlowCollector,用于包裹异常。当调用它的emit方法时,会抛出包裹的异常,代码如下:

privateclassThrowingCollector(privateval e: Throwable) : FlowCollector<Any?> {overridesuspendfunemit(value: Any?) {// 抛出异常throw e}
}
复制代码

为什么要重新创建ThrowingCollector对象,而不使用下游的FlowCollector对象呢?

为了防止当下游的流执行失败时,onCompletion方法的action参数执行时调用emit方法发送数据,这样会导致onCompletion方法作为在“finially代码块”使用时不是最后执行的方法。onCompletion方法搭配与catch方法,实现try-catch-finially代码块的效果。

三. retryWhen方法

retryWhen方法与catch方法类似,都可以用于捕获上游流产生的异常。但两者不同之处在于,retryWhen方法还可以根据“异常类型”和“重试次数”来决定是否要再次触发上游流的执行,而且当retryWhen方法不打算再次触发上游流的执行时,捕获的异常会被抛出,代码如下:

// 参数cause表示捕获到的异常// 参数attempt表示重试的次数// 参数predicate返回true表示重新触发上游流的执行publicfun<T> Flow<T>.retryWhen(predicate: suspendFlowCollector<T>.(cause: Throwable, attempt: Long) -> Boolean): Flow<T> =// 创建一个Flow对象flow {// 记录重试次数var attempt = 0L// 表示是否重新触发var shallRetry: Booleando {// 复位成falseshallRetry = false// 触发上游流的执行,并捕获异常val cause = catchImpl(this)// 如果捕获到异常if (cause != null) {// 用户判断,是否要重新触发if (predicate(cause, attempt)) {// 表示要重新触发shallRetry = true// 重试次数加1attempt++} else { // 如果用户不需要重新触发// 则抛出异常throw cause}}// 判断是否重新触发} while (shallRetry)}
复制代码

retryWhen方法是Flow接口的扩展方法。retryWhen方法的核心通过catchImpl方法实现对上游流的触发及异常捕获,并加入了由用户判断的重试逻辑实现。

点击下方卡片获取Android学习资料!

相关文章:

Kotlin协程:Flow的异常处理

示例代码如下&#xff1a;launch(Dispatchers.Main) {// 第一部分flow {emit(1)throw NullPointerException("e")}.catch {Log.d("liduo", "onCreate1: $it")}.collect {Log.d("liudo", "onCreate2: $it")}// 第二部分flow …...

qt下ffmpeg录制mp4经验分享,支持音视频(h264、h265,AAC,G711 aLaw, G711muLaw)

前言 MP4&#xff0c;是最常见的国际通用格式&#xff0c;在常见的播放软件中都可以使用和播放&#xff0c;磁盘空间占地小&#xff0c;画质一般清晰&#xff0c;它本身是支持h264、AAC的编码格式&#xff0c;对于其他编码的话&#xff0c;需要进行额外处理。本文提供了ffmpeg录…...

C#读取Excel解析入门-1仅围绕三个主要的为阵地,进行重点解析,就是最理性的应对上法所在

业务中也是同样的功能点实现。只是多扩展了很多代码&#xff0c;构成了项目的其他部分&#xff0c;枝干所在。但是有用的枝干&#xff0c;仅仅不超过三个主要的&#xff01;所以您仅仅围绕三个主要的为阵地&#xff0c;进行重点解析&#xff0c;就是最理性的应对上法所在了 str…...

一起Talk Android吧(第五百一十八回:在Android中使用MQTT通信五)

文章目录 知识回顾问题描述解决过程经验分享各位看官们大家好,这一回中咱们说的例子是" 在Android中使用MQTT通信五",本章回内容与前后章节内容无关联。闲话休提,言归正转,让我们一起Talk Android吧! 知识回顾 我们在前面章回中介绍了如何使用MQTT通信,包含它…...

100种思维模型之混沌与秩序思维模型-027

人类崇尚秩序与连续性&#xff0c;我们习惯于我们的日常世界&#xff0c;它以线性方式运作&#xff0c;没有不连续或突跳。 为此&#xff0c;我们学会了期望各种过程以连续方式运行&#xff0c;我们的内心为了让我们更有安全感&#xff0c;把很多事物的结果归于秩序&#xff0c…...

Java开发 - Redis初体验

前言 es我们已经在前文中有所了解&#xff0c;和es有相似功能的是Redis&#xff0c;他们都不是纯粹的数据库。两者使用场景也是存在一定的差异的&#xff0c;本文目的并不重点说明他们之间的差异&#xff0c;但会简要说明&#xff0c;重点还是在对Redis的了解和学习上。学完本…...

Python - 使用 pymysql 操作 MySQL 详解

目录创建连接 pymsql.connect() 方法的可传参数连接对象 conn pymsql.connect() 方法游标对象 cursor() 方法使用示例创建数据库表插入数据操作数据查询操作数据更新操作数据删除操作SQL中使用变量封装使用简单使用&#xff1a; import pymysqldb pymysql.connect(host,user…...

机器学习-卷积神经网络CNN中的单通道和多通道图片差异

背景 最近在使用CNN的场景中&#xff0c;既有单通道的图片输入需求&#xff0c;也有多通道的图片输入需求&#xff0c;因此又整理回顾了一下单通道或者多通道卷积的差别&#xff0c;这里记录一下探索过程。 结论 直接给出结论&#xff0c;单通道图片和多通道图片在经历了第一…...

考研复试——计算机组成原理

文章目录计算机组成原理1. 计算机系统由哪两部分组成&#xff1f;计算机系统性能取决于什么&#xff1f;2. 冯诺依曼机的主要特点&#xff1f;3. 主存储器由什么组成&#xff0c;各部分有什么作用&#xff1f;4. 什么是存储单元、存储字、存储字长、存储体&#xff1f;5. 计算机…...

硬件设计 之摄像头分类(IR摄像头、mono摄像头、RGB摄像头、RGB-D摄像头、鱼眼摄像头)

总结一下在机器人上常用的几种摄像头&#xff0c;最近在组装机器人时&#xff0c;傻傻分不清摄像头的种类。由于本人知识有限&#xff0c;以下资料都是在网上搜索而来&#xff0c;按照摄像头的分类整理一下&#xff0c;供大家参考&#xff1a; 1.IR摄像头&#xff1a; IRinfr…...

PTA:C课程设计(2)

山东大学&#xff08;威海&#xff09;2022级大一下C习题集&#xff08;2&#xff09;2-5-1 字符定位函数&#xff08;程序填空题&#xff09;2-5-2 判断回文&#xff08;程序填空题&#xff09;2-6-1 数字金字塔(函数)2-6-2 使用函数求最大公约数(函数)2-6-3 使用函数求余弦函…...

第四章:面向对象编程

第四章&#xff1a;面向对象编程 4.1&#xff1a;面向过程与面向对象 面向过程(POP)与面向对象(OOP) 二者都是一种思想&#xff0c;面向对象是相对于面向过程而言的。面向过程&#xff0c;强调的是功能行为&#xff0c;以函数为最小单位&#xff0c;考虑怎么做。面向对象&…...

Linux 安装npm yarn pnpm 命令

下载安装包 node 下载地址解压压缩包 tar -Jxf node-v19.7.0-linux-x64.tar.xz -C /root/app echo "export PATH$PATH:/app/node-v16.9.0-linux-x64" >> /etc/profile source /etc/profile ln -sf /app/node-v16.9.0-linux-x64/bin/npm /usr/local/bin/ ln -…...

linux SPI驱动代码追踪

一、Linux SPI 框架概述 linux系统下的spi驱动程序从逻辑上可以分为3个部分: SPI Core&#xff1a;SPI Core 是 Linux 内核用来维护和管理 spi 的核心部分&#xff0c;SPI Core 提供操作接口&#xff0c;允许一个 spi master&#xff0c;spi driver 和 spi device 在 SPI Cor…...

Ls-dyna材料的相关学习笔记

Elastic Linear elastic materials -Isotropic:各向同性材料 -orthotropic 正交各向异性的 -anistropic 各向异性的...

Arrays方法(copyOfRange,fill)

Arrays方法 1、Arrays.copyOfRange Arrays.copyOfRange的使用方法 功能&#xff1a; 将数组拷贝至另外一个数组 参数&#xff1a; original&#xff1a;第一个参数为要拷贝的数组对象 from&#xff1a;第二个参数为拷贝的开始位置&#xff08;包含&#xff09; to&#xff1a;…...

AcWing - 蓝桥杯集训每日一题(DAY 1——DAY 5)

文章目录一、AcWing 3956. 截断数组&#xff08;中等&#xff09;1. 实现思路2. 实现代码二、AcWing 3729. 改变数组元素&#xff08;中等&#xff09;1. 实现思路2. 实现代码三、AcWing 1460. 我在哪&#xff1f;&#xff08;简单&#xff09;1. 实现思路2. 实现代码四、AcWin…...

RHCSA-文件的其他命令(3.7)

目录 文件的其他命令&#xff1a; 文本内容统计wc 移动和复制&#xff08;cp&#xff09; 移动 查找文件的路径 压缩和解压缩 .tar&#xff08;归档命令&#xff09; shell-命令解释器 linux中的特殊字符 查看系统上的别名&#xff1a;alias 历史命令&#xff08;his…...

多线程update导致的mysql死锁问题处理方法

最近想起之前处理过的一个mysql 死锁问题&#xff0c;是在高并发下update批量更新导致的&#xff0c;这里探讨一下发生的原因&#xff0c;以及解决办法&#xff1b; 发生死锁的sql语句如下&#xff0c;其中where条件后的字段是有复合索引的。 update t_push_message_device_h…...

SpringBoot 如何保证接口安全?

为什么要保证接口安全对于互联网来说&#xff0c;只要你系统的接口暴露在外网&#xff0c;就避免不了接口安全问题。 如果你的接口在外网裸奔&#xff0c;只要让黑客知道接口的地址和参数就可以调用&#xff0c;那简直就是灾难。举个例子&#xff1a;你的网站用户注册的时候&am…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...