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

Kotlin协程之异常处理(launch和async的异常处理机制详解)

一 异常传播机制

  1. 先将异常传递给子协程,取消其所有子线程;(取消级联

  2. 而后取消自身;

  3. 再将异常传递给父协程,父协程接收到异常后,会取消其下所有子协程之后取消自身,再向上传递,直到到达最顶层协程。(异常的向上传递

二 异常处理机制

(1)try-catch

        在kotlin协程中,try-catch的使用是有限制的,不是任何情况都能生效的,例如在launch或async外使用try-catch进行包装,是捕获不到异常的!

一般在协程内部或挂起函数外部(包裹挂起函数)使用

(2)CoroutineExceptionHandler(官方推荐的协程异常处理)

        CoroutineExceptionHandler用于捕获未处理的异常,可以在协程作用域创建时或协程启动时作为上下文传入。

三 不同协程构建器启动的协程的默认异常处理

下面讨论的都是不进行任何异常处理和特殊操作的情况

(1)launch启动的协程

launch启动的协程遇到异常时,默认情况下会直接抛出

最终导致协程所在的协程树都收到影响,即同级协程、子协程、和父级协程都收到影响

(2)async启动的协程

①分为两种情况:异常不一定会抛出,可能被丢弃

  • async作为根协程,即没有父协程:

    遇到异常时,依赖用户消费。

    • 情况1:用户不调用await(),则异常被丢弃,不会抛出。

    • 情况2:用户调用await(),则异常直接在await()处抛出。

  • async作为子协程,即有父协程:

    遇到异常时,不依赖用户消费。

    • 情况3:用户不调用await(),根据异常传播机制,会传递给其子协程后取消自己,再将异常传递给父协程,自下而上由各级父协程决定如何进行异常处理,若父协程是launch启动的且没有进行异常处理,则异常会直接在父协程处抛出;若父协程是async启动的,则返回①继续判断...

    • 情况4:用户调用await(),异常会先向上传递,再在await()调用处抛出,故即使在await()调用处进行异常捕获,也无法阻止异常继续向上传递(若想避免这种情况,可以使用SupervisorJob上下文 或 supervisorScope 限制异常的向上传递),即异常还是会传递到父协程,若父协程是launch启动的且没有进行异常处理,则程序会抛出异常;若父协程是async启动的,则返回①继续判断...

(3)上述各情况示例代码运行结果

情况1:
@Testfun coroutineExceptionTest5(): Unit = runBlocking {val deferred = GlobalScope.async {println("父async协程")async {delay(200)println("子async协程")}throw Exception("父async协程 异常")}delay(400)}//运行结果
父async协程
情况2:
@Testfun coroutineExceptionTest5(): Unit = runBlocking {val deferred = GlobalScope.async {println("父async协程")async {delay(200)println("子async协程")}throw Exception("父async协程 异常")}delay(400)//调用await()方法deferred.await()}//运行结果
父async协程父async协程 异常
java.lang.Exception: 父async协程 异常at com.yl.activitylifecycletest.CoroutineExceptionTest$coroutineExceptionTest5$1$deferred$1.invokeSuspend(CoroutineExceptionTest.kt:122)...
情况3:
父协程是launch启动的
 @Testfun coroutineExceptionTest7(): Unit = runBlocking {GlobalScope.launch {println("父launch协程")async {delay(200)println("子async协程")throw Exception("子async协程 异常")}//不调用await()}delay(400)}//运行结果:由于父协程是launch且没有进行异常处理,故直接抛出异常
父launch协程
子async协程
Exception in thread "DefaultDispatcher-worker-1 @coroutine#3" java.lang.Exception: 子async协程 异常at com.yl.activitylifecycletest.CoroutineExceptionTest$coroutineExceptionTest7$1$1$1.invokeSuspend(CoroutineExceptionTest.kt:152)at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [CoroutineId(2), "coroutine#2":StandaloneCoroutine{Cancelling}@735d482b, Dispatchers.Default]
父协程是async启动的
 @Testfun coroutineExceptionTest6(): Unit = runBlocking {GlobalScope.async {println("父async协程")async {delay(200)println("子async协程")throw Exception("子async协程 异常")}//不调用await()}delay(400)}//运行结果:由于父协程是async是根协程,且没有调用await,故异常被丢弃,不抛出
父async协程
子async协程
情况4:
父协程是launch启动的
在await()调用处不进行try-catch进行异常捕获
@Testfun coroutineExceptionTest8(): Unit = runBlocking {GlobalScope.launch {println("父launch协程")val deferred = async {delay(200)println("子async协程")throw Exception("子async协程 异常")}//调用await()deferred.await()}delay(400)}//运行结果:调用await(),异常直接在await()调用处抛出
父launch协程
子async协程
Exception in thread "DefaultDispatcher-worker-1 @coroutine#2" java.lang.Exception: 子async协程 异常at com.yl.activitylifecycletest.CoroutineExceptionTest$coroutineExceptionTest8$1$1$deferred$1.invokeSuspend(CoroutineExceptionTest.kt:166)at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
使用try-catch在await()调用处进行异常捕获,异常也还是会向上传递
@Testfun coroutineExceptionTest8(): Unit = runBlocking {GlobalScope.launch(/*CoroutineExceptionHandler(){_,t->println("CoroutineExceptionHandler 捕获异常 $t")}*/) {println("父launch协程")val deferred = async {delay(200)println("子async协程")throw Exception("子async协程 异常")}//调用await(),并使用try-catch进行异常捕获try {deferred.await()}catch (e:Exception){println("try catch 捕获异常 $e")}}delay(400)}//运行结果:由于异常是先向上传递再在await调用处抛出的,
//故即使try-catch捕获到了异常,但是异常还是向上传递到父协程,
//又因为父协程是launch启动的,且没有进行异常处理,故而异常还是抛出父launch协程
子async协程
try catch 捕获异常 java.lang.Exception: 子async协程 异常
Exception in thread "DefaultDispatcher-worker-1 @coroutine#2" java.lang.Exception: 子async协程 异常at com.yl.activitylifecycletest.CoroutineExceptionTest$coroutineExceptionTest8$1$1$deferred$1.invokeSuspend(CoroutineExceptionTest.kt:168)at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [CoroutineId(2), "coroutine#2":StandaloneCoroutine{Cancelling}@6e8881d6, Dispatchers.Default]
在父协程中进行异常处理,捕获未处理的异常,异常不会抛出
 @Testfun coroutineExceptionTest8(): Unit = runBlocking {GlobalScope.launch(CoroutineExceptionHandler() { _, t ->println("CoroutineExceptionHandler 捕获异常 $t")}) {println("父launch协程")val deferred = async {delay(200)println("子async协程")throw Exception("子async协程 异常")}//调用await(),并使用try-catch进行异常捕获try {deferred.await()} catch (e: Exception) {println("try catch 捕获异常 $e")}}delay(400)}//运行结果:在父协程进行了异常处理,异常被父协程成功捕获拦截,不会抛出
父launch协程
子async协程
try catch 捕获异常 java.lang.Exception: 子async协程 异常
CoroutineExceptionHandler 捕获异常 java.lang.Exception: 子async协程 异常
 使用supervisorScope限制异常的向上传递,异常不会传递到父协程抛出
@Testfun coroutineExceptionTest8(): Unit = runBlocking {GlobalScope.launch(/*CoroutineExceptionHandler() { _, t ->println("CoroutineExceptionHandler 捕获异常 $t")}*/) {println("父launch协程")//使用supervisorScope 限制异常向上传递supervisorScope {val deferred = async {delay(200)println("子async协程")throw Exception("子async协程 异常")}//调用await(),并使用try-catch进行异常捕获try {deferred.await()} catch (e: Exception) {println("try catch 捕获异常 $e")}}}delay(400)}//运行结果:异常没有向上传递到父launch协程抛出父launch协程
子async协程
try catch 捕获异常 java.lang.Exception: 子async协程 异常
父协程是async启动的
调用子async协程的await(),并对其进行try-catch异常捕获
 @Testfun coroutineExceptionTest9(): Unit = runBlocking {GlobalScope.async() {println("父async协程")val deferred = async {delay(200)println("子async协程")throw Exception("子async协程 异常")}//调用await()try {deferred.await()}catch (e:Exception){println("try-catch 捕获异常 $e")}}delay(2000)}//运行结果:此处我们使用try-catch对await()进行包装,捕获到了其抛出的异常,
//由于父协程并没有调用await(),故异常不会抛出导致应用崩溃。
父async协程
子async协程
try-catch 捕获异常 java.lang.Exception: 子async协程 异常
调用子async协程的await(),但不对其进行try-catch异常捕获,也不会导致应用崩溃
 @Testfun coroutineExceptionTest10(): Unit = runBlocking {GlobalScope.async() {println("父async协程")val deferred = async {delay(200)println("子async协程")throw Exception("子async协程 异常")}//调用await()deferred.await()}delay(2000)}//运行结果:是不是很出乎意料~居然没有抛出异常导致应用崩溃!
//但是我们刚刚进行异常捕获的时候,明明在deferred.await()处是可以捕获到异常的
//猜测原因:
//deferred.await()处位于父协程内部,故而抛出异常的位置是在于父协程内部的,
//而GlobalScope有默认的全局异常处理器CoroutineExceptionHandler,进行异常处理了,
//故而没有抛异常!父async协程
子async协程

若有错误,望请指正!

相关文章:

  • TinyPro 1.2.0 正式发布:增加综合搜索,解决数据筛选难题,后端单测覆盖率再提升!
  • AOSP14 Launcher3——最近任务TaskViewSimulator详解
  • WPS JS宏编程教程(从基础到进阶)-- 第八部分:字符串技术与WPS结合应用
  • AI应用开发之扣子第一课-夸夸机器人
  • 《Vue3学习手记2》
  • SiC JFET Cascode运行原理
  • 深度学习占用大量内存空间解决办法
  • RVOS-7.实现抢占式多任务
  • 根据关键字搜索日志内容,常用的Linux命令
  • 怎么知道ip是内网还是外网?简单高效判断法
  • 基于坐标的神经表示实现零样本学习以用于快速三维多参数定量磁共振成像|文献速递-深度学习医疗AI最新文献
  • JAVA面试汇总(一)Java基础知识
  • MySQL中的公用表表达式CTE实战案例应用
  • 调度算法的学习
  • 【高中数学/指数/对数】同构六君子之 x/e^x/lnx组合曲线
  • Git_获取GitLab的token方法(访问令牌)
  • map 中key 是否可以放置的自定义的对象?
  • 【嵌入式系统设计师(软考中级)】第一章:计算机系统基础知识(上)
  • linux命令八
  • SparkSQL Join的源码分析
  • 对话地铁读书人|来自法学副教授的科普:读书日也是版权日
  • 轻流科技薄智元:AI时代,打造“工业智造”需要“共生式进化”
  • 北京一季度GDP为12159.9亿元,同比增长5.5%
  • 江西修水警方:一民房内发生刑案,犯罪嫌疑人已被抓获
  • 核观察|为核潜艇打造“安全堡垒”,印度系统性提升海基核威慑力
  • 马上评|上海市领导调研外贸外资企业,为何强调这三句话