Android 面试之Kotlin 协程上下文和异常处理

本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点

上下文是什么

CoroutineContext是一组用于定义协程行为的元素,包括以下几部分:

  • Job:控制协程的生命周期
  • CoroutineDispatcher:向合适的线程分发任务
  • CoroutineName:协程的名称,调试的时候很有用
  • CoroutineExceptionHandler:处理未被捕获的异常
  • 这几个部分可以通过"+"来组合
@Test
fun `test coroutine context`() = runBlocking {launch(Dispatchers.IO + CoroutineName("test")) {println("thread: ${Thread.currentThread().name}")}
}
协程上下文的继承
  • 对于新创建的协程,它的CoroutineContext会包含一个全新的Job实例,它会帮助我们控制协程的生命周期。
  • 剩下的元素会从CoroutineContext的父类继承,该父类可能是另外一个协程或者创建该协程的CoroutineScope

协程的上下文 = 默认值 + 继承的CoroutineContext + 参数

  • 一些元素包含默认值:Dispatchers.Default是默认的CoroutineDispatcher,以及“coroutine”作为默认的CoroutineName
  • 继承的CoroutineContext是CoroutineScope或是其父协程的CoroutineContext
  • 传入协程构建器的参数的优先级高于继承的上下文参数,因此会覆盖对应的参数值
@Test
fun `test coroutine context extend`() = runBlocking {val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable -> println("handle exception: $throwable")}//scope有了新的CoroutineContext,和runBlocking不一样val scope = CoroutineScope(Job() + Dispatchers.Main + coroutineExceptionHandler)//job的CoroutineContext继承自scope,但是Job会是新的,每个协程都会有新的Jobval job = scope.launch(Dispatchers.IO) { //新协程}
}

由于传入协程构建器的参数优先级更高,所以job的调度器被覆盖,是Dispatchers.IO而不是父类的Dispatchers.Main

异常
异常的传播

协程构建器有2种传播形式:

  • 自动传播异常(launch和actor)、向用户暴露异常(async和produce)
  • 当这些构建器用于创建一个根协程时(该协程不是另一个协程的子协程),前者这类构建器异常发生时会第一时间被抛出,而后者则依赖用户来最终消费异常,例如通过调用await或receive
  • 非根协程产生的异常总是被传播
异常传播的特性

当一个协程由于一个异常而运行失败时,它会传播这个异常并传递给它的父级。接下来父级会进行下面几步操作:

  • 取消它自己的子级协程
  • 取消它自己
  • 将异常传播并传递给它的父级
SupervisorJob和SupervisorScope
  • 使用SupervisorJob时,一个子协程的运行失败不会影响其他的子协程,SupervisorJob不会传播异常给它的父级,它会让子协程自己处理异常
  • 或者SupervisorScope中的子协程,一个失败,其他的子协程也不会受影响,但如果是协程作用域里面有异常失败,则所有子协程都会失败退出
异常的捕获
  • 使用CoroutineExceptionHandler对协程的异常进行捕获
  • 时机:异常是被自动抛出异常的协程抛出的(使用launch,而不是async时)
  • 位置:在CoroutineScope的CoroutineContext中或在一个根协程中(CoroutineScope或者supervisorScope的直接子协程)中
  • handler要安装在外部协程中,不能在内部协程中,否则捕获不到异常
@Test
fun `test exception handler`() = runBlocking {val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->println("handle exception: $throwable")}val scope = CoroutineScope(Job())//能捕获到异常val job1 = scope.launch(coroutineExceptionHandler) {launch { throw IllegalArgumentException()}}val job2 = scope.launch() {//不能捕获到异常launch(coroutineExceptionHandler) { throw IllegalArgumentException()}}
}
Android中全局异常处理
  • 全局异常处理器可以获取到所有协程未处理的未捕获异常,不过它不能对异常进行捕获。虽然不能阻止程序奔溃,全局异常处理器在程序调试和异常上报等场景中仍然有非常大的用处
  • 我们需要在classpath下面创建META-INF/services目录,并在其中创建一个名为kontlinx.coroutines.CoroutineExceptionHandler的文件,文件内容就是我们的全局异常处理器的全类名
class GlobeCoroutineExceptionHandler : CoroutineExceptionHandler {override val key = CoroutineExceptionHandleroverride fun handleException(context: CoroutineContext, exception: Throwable) {Log.d("xxx", "unHandle exception: $exception")}
}

然后再main目录下,新建resources/META-INF/services目录,然后新建kontlinx.coroutines.CoroutineExceptionHandler文件,内容为:

com.example.kotlincoroutine.GlobeCoroutineExceptionHandler
取消与异常处理
  • 取消与异常紧密相关,协程内部使用CancellationException来取消异常,但这个异常会被忽略
  • 当子协程被取消时,不会取消它的父协程
  • 如果一个协程遇到了CancellationException以外的异常,它将使用该异常取消它的父协程。当父协程的所有子协程都结束后,异常才会被父协程处理
//取消与异常
/*
* 打印顺序为:
* section 3
* section 1
* section 2
* handle exception:ArithmeticException
* 
* */
@Test
fun `test exception handler2`() = runBlocking {val handler = CoroutineExceptionHandler { _, throwable ->println("handle exception: $throwable")}val job = GlobalScope.launch(handler) {launch {try {delay(Long.MAX_VALUE)}finally {withContext(NonCancellable){println("section 1")delay(100)println("section 2")}}}launch {delay(10)println("section 3")throw ArithmeticException()}}job.join()
}
异常的聚合
  • 当协程的多个子协程因为异常而失败时,一般情况下取第一个异常进行处理。在第一个异常之后发生的所有其他异常,都将被绑定到第一个异常之上。
  • 其他异常信息可以通过exception.suppressed.contentToString来打印出来

欢迎关注我的公众号,和我一起每天进步一点点!
这里写图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1419845.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

没有公网ip,如何实现外网访问内网?

目前拨号上网是最广泛的上网方式,这种方式优点是价格便宜,缺点是没有固定公网ip,每次重新您拨号ip地址都会变。如果有一台服务器,需要实现外网访问,在没有固定公网ip的环境下,该如何实现呢?使用…

进程间通信(二)

共享内存 当进程A和进程B有一块共享的内存空间时,这两个进程之间的数据交互就会变的很简单,只需要像读取自己内存空间中的元素一样去读取数据即可。实现共享内存进行数据交互的一般步骤: 创建/打开共享内存内存映射数据交换断开与共享内存的…

驾驭多云环境,加速AI创新丨Animbus Cloud 8.3.0 算力调度平台升级发布

大模型开启全球新一轮AI浪潮,伴随算力规模的爆发增长以及计算技术的多元创新,需要更稳定、高效、敏捷的异构计算基础设施,才能充分发挥对算力能力的重要支撑。 作为开放智能云边架构引领者,九州未来凭借多年的技术积累、实践沉淀…

巨坑啊! before-upload返回false 会执行on-remove

通过对on-remove对应参数的打印,发现回调中的file参数有个status,若是是在before-upload中就被过滤了,就是ready,若是已经上传成功了去点击删除,status是success,就他了。 onRemove(file,fileList){if(file…

ESP32重要库示例详解(一):EEPROM之Preferences库

1. 了解EEPROM 在嵌入式系统开发中,断电后晚能存储少量数据是常见需求。EEPROM(Electrically Erasable Programmable Read-Only Memory)是一种非易失性存储器,即使断电数据也不会丢失。ESP32的EEPROM模拟功能利用闪存空间&#x…

Consul踢除失效服务和移除Node节点

1.consul描述 在Consul日常维护中,由于Consul不会自动将不可用的服务实例注销掉和移除node节点. 在实际使用过程中,可能因为一些操作失误、环境变更等原因让Consul中存在一些无效实例信息,而这些实例在Consul中会长期存在,并处于断开状态。…

优化资源利用,用C++内存池点亮编程之路

内存池介绍(Memory Pool): 它是一种内存分配方式,通过预先分配和复用内存块。 在真正使用内存之前,先申请一大块内存备用。当有新的内存需求时,就从内存池中分出一部分内存块, 若内存块不够再继续申请新的内存。如果我们不需要…

进程间通信(一)

IPC 在之前我们也有涉及到进程间通信的知识点,比如fork或exec或父进程读取子进程的退出码等,但是这种通信方式很有限,今天来学习进程间通信的其他技术——IPC(InterProcess Communication)。 IPC的方式通常有管道&…

JS代码随想录(一):数组

代码随想录 一、数组理论基础 二、LeetCode 704. 二分查找 三、LeetCode 27. 移除元素 四、LeetCode 977.有序数组的平方 五、LeetCode 209.长度最小的子数组 六、LeetCode 59.螺旋矩阵II 七、数组总结 一、数组理论基础 数组是存放在连续内存空间上的相同类型数据的集合。 数组…

vue3+TS或JS, 实现粒子特效 @tsparticles/vue3

在跟着B站视频BV11s4y1a71T学习时,使用到了粒子效果,但是以下这种情况只适用于项目是基于typescript的写法,否则无法实现。 粒子效果 VUE3TStsparticles/vue31、安装2、main.ts 引入3、App.vue4、效果 VUE3JS非最新版1、安装低版本的vue3-pa…

生信人写程序1. Perl语言模板及配置

生物信息领域常用语言 个人认为:是否能熟悉使用Shell(项目流程搭建)R(数据统计与可视化)Perl/Python/Java…(胶水语言,数据格式转换,软件间衔接)三门语言是一位合格生物信息工程师的标准。 生物信息常用语言非常广泛,我常用的有…

5/11后面部分:+顺序排序+元素交换+计算每门课程的各种成绩+存放规律的数据 注意:一味的复制肯定要出问题,第2个的最后一部分有修改,注意观察

目录 第一个已经输出过一次: 第二个: 编程实现:程序功能是用起泡法对数组中n个元素按从大到小的顺序进行排序。 ​编辑的确出现了一些问题哦: ​编辑目前是可以运行,但AI不给我们通过: 最后还是我的代码获胜&#x…

SAP-CentralFinance - 会计核算中的组织要素 - 学习心得1

1. 定义SAP组织架构和理解各组织架构含义 组织结构遍布SAP 系统的所有重要功能范围。FI 中最重要的组织要素是公司代码。它是“财务会计”中的最小组织单位,可以为其编制自主式完整科目集供外部报告使用。其他重要的组织要素是利润中心业务范围和段。您可以为各个利润中…

基于Vue3与ElementUI Plus的酷企秀场景可视化DIY设计器探索(更新版)

一、引言 在当今数字化快速发展的时代,企业对于展示自身形象、产品细节以及提升客户体验的需求日益增强。酷企秀场景可视化DIY设计器,以其强大的功能和灵活的定制性,为企业提供了从VR全景展示到地图可视化、电子画册制作等一系列数字化解决方…

vue3使用高德地图

一、获取高德地图key和秘钥 1、注册高德开放平台账号 #高德地图开放平台地址 https://lbs.amap.com/2、创建应用和key(选择web端) 二、安装vuemap/vue-amap库 库地址:https://vue-amap.guyixi.cn/zh-cn/introduction/install.html // 安装核心库 npm install vu…

[ue5]编译报错:使用未定义的 struct“FPointDamageEvent“

编译报错,错误很多,但很明显核心问题是第一个:使用未定义的 struct“FPointDamageEvent“: 程序没有找到FPointDamageEvent的定义。 解决办法: 处理这类未定义都可以先F12,找到它的库位置,之后…

py黑帽子学习笔记_网络编程工具

tcp客户端 socket.AF_INET表示使用标准IPV4地址和主机名 SOCK_STREAM表示这是一个TCP客户端 udp客户端 udp无需连接,因此不需要client.connect这种代码 socket.SOCK_DGRAM是udp的 tcp服务端 server.listen(5)表示设置最大连接数为5 发现kill server后端口仍占用…

【Spring Boot】玩转基础 (一篇就够了)

目录 资源 项目地址 PS 一、新建 SpringBoot 项目 1.我这里连接了码云仓库 2.新建项目 2.1不用码云的的创建方式 2.2使用码云的创建方式 3.使用 Spring InitiaIizr 创建项目 4.选择基本 Dependencies 依赖项 5.设置项目与文件编码格式 UTF-8 6.观察我们的项目架构 7.…

论文 学习 Transformer : Attention Is All You Need

目录 概述: 对摘要的理解: 框架解析 按比例缩放的点积注意力 多头注意力机制 前馈神经网络与位置编码 概述: transformer 是一个encoder ——decoder 结构的用于处理序列到序列转换任务的框架,是第一个完全依赖自注意力机制…

软件测试基础知识必备之浅谈单元测试

什么是单元测试? 单元测试是指,对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作,这里的最小可测试单元通常是指函数或者类。 单元测试都是以自动化的方式执行,所以在大量回归测试的场景下更能带来…