接下来,本文将会解释异常是如何在协程间传播的,以及一些处理它们的方法,从而帮您做到一切尽在掌握。...△ 协程中的异常会通过协程的层级不断传播 虽然在一些情况下这种传播逻辑十分合理,但换一种情况您可能就不这么想了。...处理异常 协程使用一般的 Kotlin 语法处理异常: try/catch 或内建的工具方法,比如 runCatching (其内部还是使用了 try/catch) 前面讲到,所有未捕获的异常一定会被抛出...这就是为什么没有必要将它也包裹进 try/catch 中,await 将会抛出 async 协程中产生的所有异常。...⚠️ 在 coroutineScope builder 或在其他协程创建的协程中抛出的异常不会被 try/catch 捕获!
因为在这种情况下,每个新的协程总是被分配一个新的 Job,这个新的 Job 覆盖了 SupervisorJob 。SupervisorJob 是父协程通过 scope.launch 创建的。...scope,并导致所有由其启动的协程被取消。...异常的处理 在协程中,可以使用常规语法来处理异常:try/catch 或者内置的函数 runCatching (内部使用了 try/catch) 。 我们之前说过 未捕获的异常始终会被抛出 。...CoroutineExceptionHandler 协程异常处理器 CoroutineExceptionHandler 是 CoroutineContext 中的一个可选元素,它可以帮助你 处理未捕获异常...当你要避免因异常自动传播造成的协程取消时,记住使用 SupervisorJob ,否则请使用 Job 。 未捕获异常将会被传播,捕获它们,提供良好的用户体验!
---- 异常传播流程 默认情况下,任意一个协程发生异常时都会影响到整个协程树,而异常的传递通常是双向的,也即协程会向子协程与父协程共同传递,如下方所示: 整体流程如下: 先 cancel 子协程 取消自己...但大多数情况下,tryCatch 依然如万金油一般,稳定且可靠。...但需要注意的是,CoroutineExceptionHandler 仅在未捕获的异常上调用,也即这个异常没有任何方式处理时(比如在源头tryCatch了),由于协程是结构化的,当子协程发生异常时,它会优先将异常委托给父协程区处理...默认情况下,如果 异常没有被处理,而且顶级协程 CoroutineContext 中没有携带 CoroutineExceptionHandler ,则异常会传递给默认线程的 ExceptionHandler...Log.e("petterp", "自动捕获所有异常") } val ktxScope = CoroutineScope(SupervisorJob() + exceptionHandler) 参考 协程中的取消和异常
,SupervisorJob不会传播异常给它的父级,它会让子协程自己处理异常 或者SupervisorScope中的子协程,一个失败,其他的子协程也不会受影响,但如果是协程作用域里面有异常失败,则所有子协程都会失败退出...异常的捕获 使用CoroutineExceptionHandler对协程的异常进行捕获 时机:异常是被自动抛出异常的协程抛出的(使用launch,而不是async时) 位置:在CoroutineScope...的CoroutineContext中或在一个根协程中(CoroutineScope或者supervisorScope的直接子协程)中 handler要安装在外部协程中,不能在内部协程中,否则捕获不到异常...) { throw IllegalArgumentException() } } } Android中全局异常处理 全局异常处理器可以获取到所有协程未处理的未捕获异常...当协程的多个子协程因为异常而失败时,一般情况下取第一个异常进行处理。
协程作用域的cancel 借助协程作用域的管理,我们可以轻松的控制该协程作用域下的所有协程,一旦取消一个协程作用域,那么这个协程作用域下的所有协程都将被取消。...这就是协程结构化并发的两个特点: 取消一个协程作用域,将取消该协程作用域下的所有子协程 被取消的子协程,不会影响其它同级的协程 在Android开发中,大部分场景下我们不需要考虑协程的cancel,借助...: 发生异常的协程被cancel 异常传递到它的父协程 父协程cancel(取消其所有子协程) 将异常在协程树上进一步向上传播 这种行为实际上是符合协程结构化并发的规则的,但是在实际使用中,这种结构化的异常处理...而CoroutineScope也很有用,因为你可以在一个协程发生异常时,取消其关联的所有协程,做为统一的处理。...,那么异常将被继续传递直到抛出,但如果设置了CoroutineExceptionHandler,那么则可以在这里处理未捕获的异常,CoroutineExceptionHandler的创建如下所示。
.CoroutineName:协程的名字,一般输出日志用的 4.CoroutineExceptionHandler:处理未捕获的异常 协程上下文实现了运算符重载,我们可以用+号来组合一个CoroutineContext...的元素 2.一般情况下,协程体内所有的子协程,都继承至根协程,协程的继承的关系不是我们所了解的类的继承关系,而是父协程和子协程的生命周期关系,还记得我们上面举得例子么,除非在协程体内自己手动创建协程作用域...catch并没有生效,所以向用户暴露异常只适用于不同上下文(没有继承关系)的协程 三、协程的异常处理 1.如果想要一个协程出现异常后,不影响其继承关系中的其他协程,可以使用SupervisorJob...不过它并不能阻止协程的退出,只能够获取异常的信息 它使用有两个条件: 1.异常是自动抛出异常(launch) 2.实例化CoroutineScope的时候指定异常捕获器 或者 在一个根协程中 例子1...try catch 那么println("job delay")都不会执行 由例子4和例子5,我们可以推断,如果子协程有异常发生了,我们在等待时捕获异常后,根协程执行了挂起函数,那么它会直接中断,不执行挂起函数以下的代码
大家好,我叫; 本人于2020年10月加入37手游安卓团队; 目前主要负责国内相关业务开发和一些日常业务。...,不会把job取消(会打印“4”),而且异常是job2所在协程抛出来的 3、协程中异常处理的流程源码分析 3.1、协程的三层包装 第一层:launch和async返回的job,封装了协程的状态,提供取消协程的接口...//使用SupervisorJob和supervisorScope时,子协程出现未捕获异常时也不会影响父协程, //它们的原理是重写 childCancelled() 为override...,首先会取消所有子协程 2、异常属于 CancellationException 时,不会取消父协程 3、使用SupervisorJob和supervisorScope时,即主从作用域,发生异常不会取消父协程...,launch式协程对未捕获的异常只是打印异常堆栈信息,如果使用了 CoroutineExceptionHandler 的话,只会使用自定义的 CoroutineExceptionHandler 处理异常
,处理未捕获的异常,这是第三篇文章的内容 一个新协程的 CoroutineContext 是什么?...在下面的代码中,除了使用 CoroutineScope 创建新协程之外,还展示了如何在一个协程中创建多个协程。...当协程中的所有子协程都完成了任务,协程将会进入 Cancelled 状态 (isCompleted = true) 。...最终的父 CoroutineContext 的协程调度器是 Dispatchers.IO,因为它被协程构建器中的参数覆盖了。(译者注:scope.launch(Dispatchers.IO)) 。...在系列第三篇文章中我们将看到,CoroutineScope 可以拥有其他的 Job 实现类,SupervisorJob ,它会改变协程作用域的异常处理。
如何理解协程 协程是一种不同于进程和线程的存在,其本质是一种函数,同一线程中的多个协程是串行执行的,但为了理解仍然需要三者一起对比。...info: 我认为关于协程的全部,最佳参考为Google官方编写的文章: 谷歌开发者:在 Android 开发中使用协程 | 背景介绍 在安卓开发中使用协程 Kotlin 1.3版本中开始引入了一种全新处理并发的方式...在 Kotlin 中,所有协程都必须在调度器中运行,即使它们是在主线程上运行也是如此。suspend并不代表后台执行,在哪里执行由调度器决定。协程可以自行暂停,而调度器负责将其恢复。...CoroutineScope 会跟踪它使用 launch 或 async 创建的所有协程。您可以随时调用 scope.cancel() 以取消正在进行的工作(即正在运行的协程)。...不过,与调度程序不同,CoroutineScope 不运行协程。
CoroutineExceptionHandler , 用于 在协程中捕获异常 ; 异常捕获 : 在协程中 , 使用 CoroutineExceptionHandler 对协程运行过程中产生的 异常...进行捕获 , 异常满足如下两个条件才会被捕 : 异常捕获时机 : 协程 自动抛出 的异常 , 可以在协程内被捕获 ; 使用 launch 构建的协程 可以在协程中捕获异常 , 使用 async 构建的协程...在 await 处捕获异常 ; 异常捕获位置 : 在 协程作用域 CoroutineScope 或者在 根协程 中 捕获 异常 ; 1、对比 launch 和 async 创建的协程的异常捕捉示例...在上面的小节验证了 异常捕获位置 在根协程 中的情况 , 在本小节示例中 , 验证在 协程作用域 CoroutineScope 中捕获异常 ; 代码示例 : 在 协程作用域 中 , 使用 launch...val job = scope.launch(coroutineExceptionHandler) 代码 , 在协程构建器中传入了 协程异常处理器 , 因此该协程异常处理器 可捕获 子协程传递给父协程的异常
CoroutineScope 中运行这个协程,然后取消协程作用域而不是协程的 job 。...错误的使用 SupervisorJob 有时候你会使用 SupervisorJob 来达到下面的效果: 在 job 继承体系中停止异常向上传播 当一个协程失败时不影响其他的同级协程 由于协程构建器 launch...关于 Kotlin 协程异常处理最不直观的方面之一是,你不能使用 try-catch 来捕获异常。...捕获 CancellationExceptions 当协程被取消,正在执行的挂起函数会抛出 CancellationException 。这通常会导致协程发生 "异常" 并且立即停止运行。...,我们用 try-catch 代码块来捕获所有异常。
,才在子线程中执行挂起函数 ; 如果在主线程中启动协程 , 则该模式的协程就会直接在主线程中执行 ; 如果在子线程中启动协程 , 则该模式的协程就会直接在子线程中执行 ; 协程异常处理 对于不同协程构造器...分别介绍 launch 和 async 情况下的异常处理 Launch launch 方式启动的协程,异常会在发生时立刻抛出,使用 try catch 就可以将协程中的异常捕获。...或 supervisorScope 的直接子协程时,异常在调用 await 时抛出,使用 try catch 可以捕获异常: fun main() = runBlocking { val deferred...但在 Android 开发中同样不推荐这种用法,因为它的生命周期会只受整个应用程序的生命周期限制,且不能取消。...其他环境下使用协程 其他情况下的创建按照上面协程推荐的第三种方式即可
为了确保所有的协程都会被追踪,Kotlin 不允许在没有使用 CoroutineScope 的情况下启动新的协程。...注意: 协程被挂起时,系统会以抛出 CancellationException 的方式协作取消协程。捕获顶级异常 (如Throwable) 的异常处理程序将捕获此异常。...但有时候,可能会遇到稍微复杂点的问题,例如您需要在一个协程中同时处理两个网络请求,这种情况下需要启动更多协程。...下一步 本篇文章,我们探讨了如何在 Android 的 ViewModel 中启动协程,以及如何在代码中运用结构化并发,来让我们的代码更易于维护和理解。...在下一篇文章中,我们将探讨如何在实际编码过程中使用协程,感兴趣的读者请继续关注我们的更新。
有可能有的同学问了,既然它基于线程池,那我直接使用线程池或者使用 Android 中其他的异步任务解决方式,比如 Handler、RxJava等,不更好吗?...启动协程 scope.launch(Dispatchers.Unconfined) { val one = getResult(20) val...启动协程 scope.launch(Dispatchers.Unconfined) { val one = async { getResult(20) }...异常和完成 异常捕获 对比 Flow RxJava 异常 catch onError Flow 中的 catch 对应着 RxJava 中的 onError,catch 操作: lifecycleScope.launch...e: Throwable) { } 去捕获异常,不过 catch 本质上是一个扩展方法,它是对声明式捕获的封装。
,会等到所有子协程运行完毕后才结束 2.join Job的join函数,会让后面的协程等待当前的协程执行完毕后再执行 fun `test join build`() = runBlocking {...3.LAZY:只有协程被需要时,包括主动调用协程的start、join或await等函数时才会开始调度,如果调度前被取消,那么该协程将直接进入异常结束状态 4.UNDISPATCHED:协程创建后立即在当前函数调用栈中执行...,那么其所有子协程都会退出并结束 2.coroutineScope与supervisorScope supervisorScope中,一个子协程出错了,不会影响其他子协程 fun `test supervisorScope...3.协程取消的资源释放 1.可以捕获取消异常,然后在finally中释放 2.use函数,该函数只能被实现了closeable的对象使用,程序结束时,会自动调用close方法,适合文件对象 4.不能取消的任务...处于取消中状态的协程不能被挂起,如果想要协程取消后,还能调用挂起函数,我们需要将清理的代码放入NoCancellable CoroutineContext中 这样会挂起运行中的代码,并保持协程的取消中状态
CoroutineExceptionHandler:处理未捕获的异常 它们与 CoroutineContext 的关系如下图所示: Job Job 可以监测并操控协程,可以说是协程的句柄。...Executors.newCachedThreadPool().asCoroutineDispatcher()) { } CoroutineExceptionHandler CoroutineExceptionHandler 用来处理未捕获的异常...处理复杂结构的协程异常时,它仅在顶层协程中起作用。...协程中的异常 由于协程的本质是线程池的任务,并且协程本身是结构化的,这就导致它的异常处理机制与我们普通的程序完全不一样。下面将介绍我们处理协程异常需要注意的点。...由于协程的这个特点,导致了一个协程的异常会影响到其他所有的协程。如下图所示,当子协程1发生异常时,它会先会传递给父协程,再从父协程传播到子协程2和3,从而影响所有的协程。
-- 在前几篇博客示例中 , 协程中 如果出现异常 , 没有进行捕获 , 则程序直接崩溃 , 这种情况下需要进行 异常的捕获 以 避免 Android 应用程序的崩溃 ; 示例代码 : package...java.lang.IllegalArgumentException 三、Android 全局异常处理器 ---- Android 中的 全局异常处理器 , 可以 获取 所有的 协程 中产生的 没有被捕获的异常...中处理未捕获异常 " + "\n协程上下文 ${context}" + "\n抛出异常 ${exception}") } } ⑤...全局异常处理器 中处理未捕获异常 协程上下文 [StandaloneCoroutine{Cancelling}@8252a7e, Dispatchers.Default...中处理未捕获异常 , 但是程序依然崩溃 , 可以在 全局异常处理器 中获取到异常信息 ;
本次系列文章 "协程中的取消和异常" 也是 Android 协程相关的内容,我们将与大家深入探讨协程中关于取消操作和异常处理的知识点和技巧。...(Job() + Dispatchers.Main) val job = scope.launch { //新的协程 } Job Job 用于处理协程。...在下面的代码片段中,除了通过 CoroutineScope 创建新的协程,来看看如何在协程中创建更多协程: val scope = CoroutineScope(Job() + Dispatchers.Main...当所有的子协程都完成后,协程会进入已取消 (Cancelled) 状态,此时 isCompleted = true。...现在,大家了解了协程的一些基本概念,在接下来的文章中,我们将在第二篇继续深入探讨协程的取消、第三篇探讨协程的异常处理。
导读 2017 年,某业务团队通过某次技术会议确定禁止在代码中使用异常,当时的目的旨在规范一些存在的基本问题,诸如:使用异常导致协程冲突,捕获到异常和抛出的不一致;未捕获异常导致后端框架中的 worker...当时坚决反对使用异常的同事提出: 使用异常导致协程冲突,捕获到异常和抛出的不一致; 未捕获异常导致后端框架中的 worker 进程终止,重启 worker 进程漫长导致效率很差。...根据上述结果我们可以得到一些简单的结论: 任何情况下,全局变量是不安全的,只要是使用了多协程或多线程,读取和写入不具备连续性; 在不使用多线程的情况下,理论上全局变量只要保证在抛出并且捕获之间保证不发生协程切换是安全的...未捕获异常导致框架中的 worker 进程终止,重启 worker 进程漫长导致效率很差。...那么在 libco 的库的使用情况下,如何安全的使用异常呢? 只需要关心 catch 块中是否会发生协程切换,如果 catch 块中的代码确定不会发生协程切换就是安全的。
当然,这并不算是一个全局的异常捕获,因为它只能捕获对应协程内未捕获的异常,如果你想做到真正的全局捕获,在 Jvm 上我们可以自己定义一个捕获类实现: class GlobalCoroutineExceptionHandler...coroutineScope 是继承外部 Job 的上下文创建作用域,在其内部的取消操作是双向传播的,子协程未捕获的异常也会向上传递给父协程。...,出现未捕获的异常会尝试传递给父协程并尝试取消父协程。...不同之处在于, launch 中未捕获的异常与 async 的处理方式不同, launch 会直接抛出给父协程,如果没有父协程(顶级作用域中)或者处于 supervisorScope 中父协程不响应,那么就交给上下文中指定的...这一块儿稍微显得有点儿复杂,但仔细理一下主要有三条线: 协程内部异常处理流程:launch 会在内部出现未捕获的异常时尝试触发对父协程的取消,能否取消要看作用域的定义,如果取消成功,那么异常传递给父协程
领取专属 10元无门槛券
手把手带您无忧上云