Kotlin学习笔记之 32 协程异常处理

2,051 阅读1分钟

首发于公众号: DSGtalk1989

32.协程异常处理

  • 异常的传播

    launchactor构建器是不传播异常的,asyncproduce是传播异常的。这里的传播说的更容易理解一点叫做,往外抛,即不传播异常就是在本协程中自己消化,异常发生在本协程所在的线程;传播异常表示不在本协程所在线程发生,异常直接往外抛到启动该协程所在的线程。我们看如下demo

    val 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一下,针对上面的launchawait方法,我们包裹了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