Android 开发中 Kotlin Coroutines 如何优雅地处理异常

2023-11-17

一. 尽量少用 GlobalScope

GlobalScope 是 CoroutineScope 的实现类。我们以前使用过的 launch、async 函数都是 CoroutineScope 的扩展函数。

GlobalScope 没有绑定任何 Job 对象,它用于构建最顶层的协程。这些协程的生命周期会跟随着 Application。

在 GlobalScope 中创建的 Coroutines,是有可能会导致应用崩溃的。

例如:

fun main() {
    GlobalScope.launch {
        throw RuntimeException("this is an exception")
        "doSomething..."
    }
    Thread.sleep(5000)
}

即使在 main 函数中,增加了 try...catch 试图去捕获异常,下面的代码仍然抛出异常。

fun doSomething(): Deferred<String> = GlobalScope.async {
    throw RuntimeException("this is an exception")
    "doSomething..."
}
fun main() {
    try {
        GlobalScope.launch {
            doSomething().await()
        }
    } catch (e:Exception) {
    }
    Thread.sleep(5000)
}

这是因为,在 doSomething() 内创建了子 Coroutine,子 Coroutine 的异常会导致整个应用的崩溃。

接下来,通过一个自定义 CoroutineScope ,并且它的 CoroutineContext 与 Job 对象相加,Job 对象可以直接管理该协程。但是子 Coroutine 依然可能会抛出异常,从而导致应用的崩溃。

val job: Job = Job()
val scope = CoroutineScope(Dispatchers.Default+job)
fun doSomething(): Deferred<String> = scope.async {
    throw RuntimeException("this is an exception")
    "doSomething..."
}
fun main() {
    try {
        scope.launch {
            doSomething().await()
        }
    } catch (e:Exception) {
    }
    Thread.sleep(5000)
}

二. SupervisorJob、CoroutineExceptionHandler 的使用

对于 GlobalScope 创建的 Coroutines,前面已经介绍过可能会导致 Crash。

例如:

        text1.setOnClickListener {
            GlobalScope.launch(UI) {
                Toast.makeText(mContext,"cannot handle the exception", Toast.LENGTH_SHORT).show()
                throw Exception("this is an exception")
            }
        }

如果能够创建一个 CoroutineScope,由该 CoroutineScope 创建的 Coroutines 即使抛出异常,依然能够捕获,那将是多么的理想。

例如:

        text2.setOnClickListener {
            uiScope().launch {
                Toast.makeText(mContext,"handle the exception", Toast.LENGTH_SHORT).show()
                throw Exception("this is an exception")
            }
        }

如果还能够对异常做一些处理,那将是再好不过的了。

例如:

        text3.setOnClickListener {
            val errorHandle = object : CoroutineErrorListener {
                override fun onError(throwable: Throwable) {
                    Log.e("errorHandle",throwable.localizedMessage)
                }
            }
            uiScope(errorHandle).launch {
                Toast.makeText(mContext,"handle the exception", Toast.LENGTH_SHORT).show()
                throw Exception("this is an exception")
            }
        }

上面使用的 uiScope 是调用的 SafeCoroutineScope 来创建的 CoroutineScope。

SafeCoroutineScope 的 CoroutineContext 使用了 SupervisorJob 和 CoroutineExceptionHandler。

SupervisorJob 里面的子 Job 不相互影响,一个子 Job 的失败,不会不影响其他子 Job 的执行。

CoroutineExceptionHandler 和使用 Thread.uncaughtExceptionHandler 很相似。CoroutineExceptionHandler 被用来将通用的 catch 代码块用于在协程中自定义日志记录或异常处理。

我们来看一下它们的封装:

val UI: CoroutineDispatcher      = Dispatchers.Main
fun uiScope(errorHandler: CoroutineErrorListener?=null) = SafeCoroutineScope(UI,errorHandler)
class SafeCoroutineScope(context: CoroutineContext, errorHandler: CoroutineErrorListener?=null) : CoroutineScope, Closeable {
    override val coroutineContext: CoroutineContext = SupervisorJob() + context + UncaughtCoroutineExceptionHandler(errorHandler)
    override fun close() {
        coroutineContext.cancelChildren()
    }
}
class UncaughtCoroutineExceptionHandler(val errorHandler: CoroutineErrorListener?=null)  :
        CoroutineExceptionHandler, AbstractCoroutineContextElement(CoroutineExceptionHandler.Key) {
    override fun handleException(context: CoroutineContext, throwable: Throwable) {
        throwable.printStackTrace()
        errorHandler?.let {
            it.onError(throwable)
        }
    }
}

所以,点击 text2、text3 按钮时,不会导致 App Crash。

在点击 text4 时,即使子 Coroutine 抛出异常,也不会导致 App Crash

        text4.setOnClickListener {
            uiScope().launch {
                try {
                    uiScope().async {
                        throw RuntimeException("this is an exception")
                        "doSomething..."
                    }.await()
                } catch (e: Exception) {
                }
            }
            Toast.makeText(mContext,"handle the exception", Toast.LENGTH_SHORT).show()
        }

三. 在 View 中创建 autoDisposeScope

在 Android View 中创建的 Coroutines,需要跟 View 的生命周期绑定。

下面定义的 View 的扩展属性 autoDisposeScope,也是借助 SafeCoroutineScope。

// 在 Android View 中创建 autoDisposeScope,支持主线程运行、异常处理、Job 能够在 View 的生命周期内自动 Disposable
val View.autoDisposeScope: CoroutineScope
    get() {
        return SafeCoroutineScope(UI + ViewAutoDisposeInterceptorImpl(this))
    }

有了 autoDisposeScope 这个 CoroutineScope,就可以在 View 中放心地使用 Coroutines。

        text2.setOnClickListener {
            text2.autoDisposeScope.launch {
                doSomeWork()
            }
        }

四. 总结

去年在《AAC 的 Lifecycle 结合 Kotlin Coroutines 进行使用》 一文中,曾经介绍过我封装的库:https://github.com/fengzhizi715/Lifecycle-Coroutines-Extension,本文是对该库的一次升级,也是对近期使用 Kotlin Coroutines 的经验总结。

https://github.com/fengzhizi715/Lifecycle-Coroutines-Extension,不仅是对 Lifecycle 的封装,也可以说是对 Coroutines 使用的封装。

关注【Java与Android技术栈】

更多精彩内容请关注扫码

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 开发中 Kotlin Coroutines 如何优雅地处理异常 的相关文章

  • Anaconda3中torch.cuda.is_available()返回false的可能解决办法

    1 问题 在CUDA cudnn 已装好 指令 conda install pytorch torchvision torchaudio pytorch cuda 11 7 c pytorch c nvidia 一直转圈 不得已使用pip指
  • 北工大汇编——综合题(1)

    题目要求 统计字符数 从键盘输入一行字符 统计字母 空格 数字 其他宇符的个数 并显示 要求 提示输入一行宇符串 键盘输入宇符串 Enter 键结束输入 并换行显示结果 题目代码 DATAS SEGMENT 此处输入数据段代码 msg DB
  • unity编辑器详细介绍

    你好 欢迎来到UNITY 2021 世界 这里将会详细为您介绍UNITY基本操作工具 目录 一 工具栏 二 菜单栏 1 File 文件 菜单 2 Edit 编辑 菜单 3 Assets 资源 菜单 4 GameObject 游戏对象 菜单
  • Java Scanner nextInt()方法与示例

    扫描器类的nextInt 方法 Scanner Class nextInt method Syntax 句法 public int nextInt public int nextInt int rad nextInt method is a
  • 支撑区块链大规模商用,FISCO BCOS v3.0的那些“黑科技”

    文章转载自CSDN公众号 在2021年度金链盟生态大会上 全新的FISCO BCOS v3 0正式发布 该版本从架构 算法以及安全可控和隐私计算协同等方向进行了全面升级 满足数字经济时代对区块链系统可承载更大规模 更多场景 更广泛参与的产业
  • 光耦基础知识

    1 光耦 光耦合器 optical coupler 英文缩写为OC 亦称光电隔离器或光电耦合器 简称光耦 在电源当中 光耦和TL431一般都是一起出现的 它们组合一起的作用是用来控制初次级反馈环路的稳定 从而实现输出电压的稳定 有插件的 也
  • IPv6地址前缀

    当我们通过ipv6 install 命令在windows XP系统上安装了ipv6协议栈后 我们可以看到原来的网卡多了一个新的地址 ipv6地址 这个地址是基于ipv6无状态自动配置机制设定的 所有无状态自动配置的地址的前缀都是fe80 其
  • Python爬虫实现抓取腾讯视频所有电影-源码【实战必学】

    用python实现的抓取腾讯视频所有电影的爬虫 1 coding utf 8 2 import re 3 import urllib2 4 from bs4 import BeautifulSoup 5 import string time
  • 更改 华为云 yum 源

    更改 华为云 yum 源 1 安装wget yum install y wget 2 备份 mv etc yum repos d CentOS Base repo etc yum repos d CentOS Base repo bak 3
  • Linux 系统编程之select

    Linux 系统编程之select select 允许单个程序监听多个文件描述符 直到一个或者多个文件描述符准备就绪不阻塞系统 常常用于解决阻塞型的程序 相关代码 According to POSIX 1 2001 include
  • Linux之文件上传和下载(两种方式)

    前言 由于工作需要 整理了一下Linux与Windows之间文件的上传和下载方式 一 rz上传 sz下载文件 rz是将Windows系统上的文件上传到Linux下 sz是将Linux系统下的文件上传到Windows下 1 rz案例 输入rz
  • 【STM32】NRF24L01模块的收发调试

    NRF24L01 发送端 c文件 发送端 h文件 接收端 c文件 接收端 h文件 接收端main函数 总结 这里我是用了两块板子来做通信实验 这里我就直接贴发送端和接收端的 c h文件 一个是用标准库写的一个是hal库写的 只是两块板子引脚
  • python 生成随机字符串(数字+字母+特殊字符)

    方法一 usr bin env python coding utf 8 import random import string 第一种方法 seed 1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHI
  • 【数据可视化】基于Streamlit制作的时间序列数据分析APP(上手简单,附可运行源码)

    基于Streamlit制作的时间序列数据分析APP 1 为什么要使用Streamlit 官网介绍链接 在数据科学领域 一方面 当我们在学习或者实践机器学习算法的时候 需要进行一些参数调整 另一方面 我们也希望将最后的成果通过一种友好的可视化
  • Python入门:什么是爬虫,怎么玩爬虫?

    python爬虫入门 什么是爬虫 怎么玩爬虫 看到这两只爬虫没有 两只爬虫 两只爬虫 跑得快 跑得快 一只没有 不好意思 跑题了 别误会 今天不是要教你怎么玩上面这两只沙雕玩意 今天 我们正式从0到1 轻松学会 python 爬虫 接下来
  • STC单片机代码烧录

    说明 两种烧录方式 一 ISP烧录 原料 usb装ttl 单片机 开发板 stc isp 15xx v6 85H exe 群文件有 1 接线vcc vcc gnd gnd RX TX TX RX 2 安装CH340驱动 群文件有 3 将TT
  • 给电脑重装系统后修改远程桌面端口的方法

    怎么修改远程端口 我们都知道系统默认的远程桌面端口是3389 可提供 远程桌面 等连接工具来连接到远程的服务器上面 但是近期有部分Win11用户想要修改这一端口 那么应该如何操作呢 还不清楚应该如何操作的朋友们 可以试试用下面的方法进行操作
  • k8s-3:集群的三种网络

    k8s集群一共有三种网络模型 Node IP Node节点的IP地址 即物理网卡的IP地址 Pod IP Pod的IP地址 即docker容器的IP地址 此为虚拟IP地址 Cluster IP Service的IP地址 此为虚拟IP地址 N
  • 内网搭建yum源

    一 安装httpd服务 yum install y httpd systemctl start httpd systemctl status httpd 二 挂载centos7的ISO文件 2 1 提前准备好ISO文件 下载链接 https
  • chatgpt使用技巧,提示词技巧,效率提问,如何更好的使用chatgpt

    说明 什么是提示词 提示词有什么作用 提示词是我们向语言模型提出问题时使用的基本词语 使用提示词可以让模型更好地理解我们的需求 得出更准确 有用的回答 与直接向模型提问相比 使用提示词需要明确我们的要求 并提供一些背景资料和实例等详细信息

随机推荐

  • NLP实战:基于Pytorch的文本分类入门实战

    目录 一 前期准备 1 环境准备 2 加载数据 二 代码实战 1 构建词典 2 生成数据批次和迭代器 3 定义模型 4 定义实例 5 定义训练函数与评估函数 6 拆分数据集并运行模型 三 使用测试数据集评估模型 四 总结 本文为 365天深
  • 解决MarkDownPad表格显示的问题,以及代码结构显示的问题

    中文版 工具 选项 Markdown Markdown处理器 改为 Markdown 扩展 即可 选择GitHub Flavored Markdown Offling 英文版 Tools Options MarkDown Markdown
  • linux动态链接库的创建

    如下实际操作形式
  • PuTTY和WinSCP

    PuTTY和WinSCP是管理OpenWRT固件的路由器必备工具 WinSCP的优势在于支持多语言 可在图形化界面下管理Linux系统里的文件和存储多个会话设置以方便多设备多用户登录 PuTTY的优势在 于提供与Linux终端一样的命令行控
  • 互联网产品经理面试二三事

    http www masterchat cn article article 250 1 html 摘要 面试是一件辛苦的工作 相信被面试也是一件不容易的事 找到一个适合企业的人才 并不一定是最优秀的 很不容易 尤其在互联网领域 优秀的人才
  • css设置文字垂直居中

    vertical align top 不断调整该值 就可以调整上下位置哦 如 上 vertical align top 中 vertical align middle 下 vertical align bottom 特殊 vertical
  • 数据预处理--缺失值和异常值的处理

    处理缺失数据的方法 1 用平均值 中值 分位数 众数 随机值等替代 如果预计该变量对于学习模型效果影响不大 可以对unknown值赋众数 这里认为变量都对学习模型有较大影响 效果一般 因为等于人为增加了噪声 不建议采取此法 数值型的话 均值
  • 正则表达式生成器

    正则表达式生成器 智能提示和表达式含义解析 高亮显示匹配项内容 高端智能正则表达式生成工具 支持生成语言 Javascript PHP Go rb python Java objc C 等 下载地址 https download csdn
  • 剑指 Offer :001整数除法

    题目描述 给定两个整数 a 和 b 求它们的除法的商 a b 要求不得使用乘号 除号 以及求余符号 注意 整数除法的结果应当截去 truncate 其小数部分 例如 truncate 8 345 8 以及 truncate 2 7335 2
  • 线性回归案例分析-预测PM2.5

    作业说明 给定训练集train csv 要求根据前9个小时的空气监测情况预测第10个小时的PM2 5含量 训练集介绍 1 CSV文件 包含台湾丰原地区240天的气象观测资料 取每个月前20天的数据做训练集 12月X20天 240天 每月后1
  • 特征工程:特征构造以及时间序列特征构造

    数据和特征决定了机器学习的上限 而模型和算法只是逼近这个上限而已 由此可见 特征工程在机器学习中占有相当重要的地位 在实际应用当中 可以说特征工程是机器学习成功的关键 那特征工程是什么 特征工程是利用数据领域的相关知识来创建能够使机器学习算
  • Xray 漏洞扫描工具使用方法

    本文仅供学习参考 严禁在未经网站管理员允许的情况下对网站进行扫描 本文仅供学习参考 严禁在未经网站管理员允许的情况下对网站进行扫描 本文仅供学习参考 严禁在未经网站管理员允许的情况下对网站进行扫描 滥用漏扫工具 违法国家安全法后果自负 申明
  • python中的tkinter包的使用--Menubar菜单窗口

    下面的例子讲如何制作简单的菜单 初始化窗口 点击子菜单New 继续点击其他子菜单 代码 import tkinter as tk window tk Tk window title my window window geometry 200
  • abaqus运行子程序出现无法打开输入文件msmpi.lib的解决办法

    好不容易搞定了abaqus Visual studio2013 Fortran 2013的关联 进行尝试时利用子程序进行计算时 出现了以下错误 End Compiling Abaqus Standard User Subroutines B
  • 【无标题】python:将一个表格的一行或几行数据通过双击或按键方式转移到另外一个表格中去

    import sys from PyQt5 QtWidgets import from move import QtWidgets QMainWindow 继承该类方法 class Utest window QtWidgets QMainW
  • Windows下安装使用mysqldumpslow

    首先需要安装Perl 在windows下安装Perl 安装过程很简单 从官网 http strawberryperl com 下载windows安装包 安装好之后 测试perl v 如果能显示版本号 表示安装成功 mysqldumpslow
  • muduo3学习笔记——Timestamp.{h,cc}

    首先代码中的一些要点 1 Timestamp类继承自boost less than comparable 模板类 只要实现 lt 即可自动实现 gt lt gt 2 使用到了BOOST STATIC ASSERT 编译时断言 3 gmtim
  • 查验身份证 C语言

    碎碎念念 这道题很坑 注意这里的权重是直接乘上去 一开始我乘的是百分之几 死活不知道哪里有问题 后来把一百乘上去 就对了 代码 include
  • 计算机网络第一章课后习题

    1 14 计算机网络有哪些常用的性能指标 速率 带宽 吞吐量 时延 时延带宽积 往返时间RTT 利用率 1 17 收发两端之间的传输距离为1000km 信号在媒体上的传播速率为2 x 10 8 m s 试计算以下两种情况的发送时延和传播时延
  • Android 开发中 Kotlin Coroutines 如何优雅地处理异常

    一 尽量少用 GlobalScope GlobalScope 是 CoroutineScope 的实现类 我们以前使用过的 launch async 函数都是 CoroutineScope 的扩展函数 GlobalScope 没有绑定任何