23 | 异常:try-catch居然会不起作用?坑!
- 深入了解
- 翻译
- 解释
- 总结
Kotlin协程异常处理是一个复杂的技术挑战,本文通过深入分析各种场景和问题,总结出了6大准则,为读者提供了解决异常问题的有效方法。首先,协程的取消需要内部的配合,需要在协程体中加入状态判断来响应取消事件。其次,不要轻易打破协程的父子结构,否则子协程可能无法跟随父协程一起取消。文章还探讨了try-catch在Kotlin协程中的应用,指出不要直接包裹launch、async,而是将try-catch挪到协程体内部。此外,对于CancellationException的处理,需要注意捕获后是否应该重新抛出。灵活使用SupervisorJob和CoroutineExceptionHandler可以控制异常传播的范围,提供了实用的指导。总的来说,本文通过深入分析Kotlin协程异常处理的各种场景和问题,总结出了实用的准则和技巧,为读者提供了解决异常问题的有效方法。
《朱涛 · Kotlin 编程第一课》,新⼈⾸单¥59
全部留言(13)
- 最新
- 精选
- 神秘嘉Bin(1)Cancel依赖Cancel异常。 (2)SupervisorJob重写了childCancelled=false,导致取消不会向上和兄弟传播。 (3)异常的传播应该是先向上传播,然后都没人处理才会触发协程的CoroutineExceptionHandler,在触发全局默认的CoroutineExceptionHandler。 (4)ExceptionHandler代替try catch不合理,无法清晰的对业务异常有一个认知,不知道是哪里来的,只能通用处理;同时我认为ExceptionHandler或者作为兜底策略也是合理的,子协程对自己的业务进行异常处理,同时顶层协程有一个兜底策略,上报后需要及时让子协程进行处理;这个问题就像Java线程要不要加UnCauthedExceptionHandler【协程也可以加默认的】。
作者回复: 很棒的总结!
2022-03-1522 - better老师,我实验了 CoroutineExceptionHandler ,貌似可以用在局部,还是我哪里搞错了,关键点 + 了一个 Job() 就可以了; 环境:JDK: 11 , Kotlin: 1.6.10, Kotlin coroutine-core: 1.6.0 代码如下: launch { delay(100L) launch(exceptionHandler + Job() ) { // delay(100L) 1 / 0 // Cause Exp } }
作者回复: 因为你加了一个Job()以后,协程的父子关系就被打破了,这时候它其实就相当于顶层协程了。(请看本节课的第二张图。)
2022-03-14213 - Allen由于 Coroutine 是结构化的,当一个子 job 出现异常会导致其他协程都停止运行,在一些场景下并不是我们想要的。我们需要使用 try catch 来将异常捕获在发生异常的地方。 如果使用 supervisorJob 来阻止异常传递的话,CoroutineExceptionHandler 又无法接收到异常,导致我们无法知道哪一个 Job由于什么原因被中止了。这时使用 try catch 可以捕获到对应的异常信息。 言而总之,try catch 在处理协程异常时,还是很有必要的。
作者回复: 有理有据,分析的角度也很全面。
2022-03-117 - 魏全运思考题: 全部用ExceptionHandler 替代try catch是不合理的,因为在实际编码过程中,我们有时需要在当前上下文合适地处理异常,比如兜底操作,重试等,并不是一味的把异常抛出去,如果都交给顶层就无法很好地处理这些case。类似于Java的UnCauthedExceptionHandler 。
作者回复: 思路很棒,UnCauthedExceptionHandler这个类比也很恰当。
2022-03-115 - êwěn思考题: 个人理解,看场景。有些业务是可以把异常当作分支处理,这种情况,handler就不适合了。
作者回复: 很对,如果我们需要针对异常做一些特殊的业务逻辑,handler就不适合了。
2022-03-113 - dashingqi请问,文章中的动画是怎么做的啊?
作者回复: 我用PPT动画一帧一帧做的。
2022-04-25 - syz准则二例子 val job1 = Job() val pJob: Job = launch(myFixedThread) { launch(job1) {//coroutine#3} launch{//CoroutineId(4)} } 照搬的例子,测试时发现job1下的协程也停止了打印 检查发现 "coroutine#3":StandaloneCoroutine{Active}@543fc144, "coroutine#4":StandaloneCoroutine{Cancelling}@32e5773b 问题:打破协程父子结构的子协程coroutine#3(父协程是job1,当前处于Active状态)停止了打印?pJob的cancel没有影响到job1的Active状态,但是coroutine#3不再打印数据
作者回复: 如果你完全照搬代码段3的话,应该是会得出跟文章中一样的结果的。你的代码执行不符合预期,一定是有其他的改动吧?你可以把完整代码贴出来看看。
2022-04-15 - jim代码段2: launch(Dispatchers.Default) 这里为什么必须使用Default线程池?
作者回复: 因为协程内部有阻塞的行为:Thread.sleep(500L)
2022-04-062 - 追梦小乐关于异常要写对应的异常,不要用父类的Except ion,在使用快捷键给一点代码加上try-catch后,出来的就是父类的题材,那老师我们在编写代码的时候,怎么判断是属于某一个具体的异常?!
作者回复: 一方面是靠经验,另一方面是靠读源码,还有一方面是靠单元测试。
2022-03-23 - 白乾涛以下代码,"End" 都打印了,子 Job 也停止了,为啥程序不能停止? fun main() = runBlocking { val dispatcher = Executors.newFixedThreadPool(2) { Thread(it, "bqt") }.asCoroutineDispatcher() val pJob = launch(dispatcher) { launch { var i = 0 while (true) { delay(500) i++ println("cJob - $i") } } } delay(2000L) pJob.cancel() pJob.join() println("End") }
作者回复: 试试把线程池改为:守护线程。
2022-03-202