
首发于公众号: DSGtalk1989
32.协程异常处理
-
异常的传播
launch和actor构建器是不传播异常的,async和produce是传播异常的。这里的传播说的更容易理解一点叫做,往外抛,即不传播异常就是在本协程中自己消化,异常发生在本协程所在的线程;传播异常表示不在本协程所在线程发生,异常直接往外抛到启动该协程所在的线程。我们看如下demoval job = GlobalScope.launch { println("${Thread.currentThread().name} : Throwing exception from launch") throw IndexOutOfBoundsException() } job.join() println("Joined failed job") val deferred = GlobalScope.async { println("${Thread.currentThread().name} : Throwing exception from async") throw ArithmeticException() } deferred.await() println("Unreached")控制台打印
DefaultDispatcher-worker-1 : Throwing exception from launch Exception in thread "DefaultDispatcher-worker-1" java.lang.IndexOutOfBoundsException at salamanca.DestructTestKt$main$1$job$1.invokeSuspend(DestructTest.kt:52) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32) at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:233) at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594) at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742) Joined failed job DefaultDispatcher-worker-1 : Throwing exception from async Exception in thread "main" java.lang.ArithmeticException at salamanca.DestructTestKt$main$1$deferred$1.invokeSuspend(DestructTest.kt:58) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32) at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:233) at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594) at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)我们看到两个异常分别在协程的线程和主线程中抛出来一个由于在协程内,所以不会造成主线程异常,一个在主线程,所以直接跪了。
-
CoroutineExceptionHandler
所以针对上面这种情况,我们应该怎么办,比如我们想要去
catch一下,针对上面的launch和await方法,我们包裹了try catch如下try{ job.join() }catch (e : Exception){ println(e.toString()) } try{ deferred.await() }catch (e : Exception){ println(e.toString()) }发现
await是可以捕捉的,join无法用try catch捕捉。所以针对这种不传播异常的,我们应该怎么去捕捉它呢。CoroutineExceptionHandler登场。我们将上面的代码作如下改动
val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception") } val job = GlobalScope.launch(handler) { println("${Thread.currentThread().name} : Throwing exception from launch") throw IndexOutOfBoundsException() } job.join()打印如下,舒坦。
DefaultDispatcher-worker-1 : Throwing exception from launch Caught java.lang.IndexOutOfBoundsException