美文网首页
Kotlin 协程异常处理

Kotlin 协程异常处理

作者: 过期的薯条 | 来源:发表于2021-12-26 18:07 被阅读0次

    1.引言

    kotlin的异常处理很不容易理解,看了好久,好久慢慢才明白,因为有必要写一篇文章搞清楚一下问题:

    • try-catch捕获异常
    • CoroutineExceptionHandler
    • supervisorScope 和SupervisorJob

    2.正题

    先看看这个例子:

        //协程异常处理  1.在协程作用域中捕捉  2.协程异常处理器
        fun coroutineBuildRunBolck7() {
            try {
                Thread() {
                    throw NullPointerException()
                }.start()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    

    结果:运行崩溃
    try-catch 只能捕捉当前线程的堆栈信息。对于非当前线程无法实现捕捉 。既然这样下面代码应该会被捕捉到:

    fun coroutineBuildRunBolck7() = runBlocking(Dispatchers.IO) {
            try {
                launch {
                    throw NullPointerException()
                }
            } catch (e: Exception) {
                e.printStackTrace()
                Log.d("wangxuyang", "" + e.message)
            }
        }
    

    结果:运行崩溃。此时这里应该有一个大大的问号?为什么会这样?
    结论:launch启动的协程,是不会传播异常的。
    此时肯定会问什么叫传播异常?
    所谓的传播异常,是指能够将异常主动往外抛到启动顶层协程所在的线程。因为launch启动的协程,是不会将异常抛到线程,所以try-catch无法捕捉.

    为了让这种异常能够捕捉到。协程引入了CoroutineExceptionHandler

    2.2 CoroutineExceptionHandler

    CoroutineExceptionHandler是协程用来解决launch启动协程,异常不传播的问题。当出现这种异常不传播问题。协程内部是如何处理的呢?

    当一个协程发生了异常,它将把异常传播给它的父协程,父协程会做以下几件事:

    • 取消其他子协程
    • 取消自己
    • 将异常传播给自己的父协程
      异常最终将传播至继承结构的根部。通过该 CoroutineScope 创建的所有协程都将被取消。

    //插入一张图片

    让CoroutineExceptionHandler 拦截异常不传播,需要满足以下条件:
    何时 :是被可以自动抛异常的协程抛出的(launch,而不是 async)
    何地 :GlobalScope全局作用域.launch 启动一个根协程,或者自定义一个

    所以由上可以得知。要想解决上述的异常。我们得在根协程中做异常处理。

     val handler = CoroutineExceptionHandler { _, exception ->
            Log.d("wangxuyang", "CoroutineExceptionHandler")
        }
     fun coroutineBuildRunBolck7() = runBlocking(Dispatchers.IO) {
            //方式1
            GlobalScope.launch(handler) {
                launch {
                    throw NullPointerException()
                }
            }
            //方式2,
            CoroutineScope(Job()+handler)
                .launch {
                    throw NullPointerException()
                }
        }
    

    结果:打印2021-12-26 17:08:32.332 14031-14278/com.heytap.tv.myapplication D/wangxuyang: CoroutineExceptionHandler

    再看一个案例:

    fun coroutineBuildRunBolck7() = runBlocking(Dispatchers.IO) {
            GlobalScope.launch {
                launch(handler) {
                    launch {
                        throw NullPointerException()
                    }
                }
            }
        }
    

    这种依旧会崩溃。它不等同于那样try-catch到了就停止上报。协程中的传播异常,最终都会上报到根协程中。中途无法处理异常。所以上面依旧不行

    2.3 supervisorScope 和 SupervisorJob

    上面提到,当一个协程报异常了,会终止它的兄弟姐妹协程。这样很不合理。为此kotlin 协程开发团队提供了一个supervisorScope方法。用来解决这个问题。使用它,当子协程出现异常,不会影响其兄弟姐妹的协程运行

    val handler = CoroutineExceptionHandler { _, exception ->
            Log.d("wangxuyang", "CoroutineExceptionHandler")
        }
        //协程异常处理  1.在协程作用域中捕捉  2.协程异常处理器
        fun coroutineBuildRunBolck7() = runBlocking(Dispatchers.IO) {
            CoroutineScope(Job() + handler)
                .launch {
                    supervisorScope {
                        launch {
                            Log.d("wangxuyang", "start job1 delay")
                            delay(1000)
                            Log.d("wangxuyang", "end job1 delay")
                        }
                        launch {
                            Log.d("wangxuyang", "job2 throw execption")
                            throw NullPointerException()
                        }
                    }
                }
        }
    

    输出结果:
    D/wangxuyang: start job1 delay
    D/wangxuyang:job2 throw execption
    D/wangxuyang:CoroutineExceptionHandler
    D/wangxuyang:end job1 delay

    上面的例子可以使用SupervisorJob 达到同样的效果。supervisorScope ==SupervisorJob .

     //协程异常处理  1.在协程作用域中捕捉  2.协程异常处理器
    val handler = CoroutineExceptionHandler { _, exception ->
        Log.d("wangxuyang", "CoroutineExceptionHandler")
    }
    with(CoroutineScope(SupervisorJob() + handler)) {
                launch {
                    Log.d("wangxuyang", "start job1 delay")
                    delay(1000)
                    Log.d("wangxuyang", "end job1 delay")
                }
                launch {
                    Log.d("wangxuyang", "job2 throw execption")
                    throw NullPointerException()
                }
            }
    

    以上就是我对协程异常处理的理解

    相关文章

      网友评论

          本文标题:Kotlin 协程异常处理

          本文链接:https://www.haomeiwen.com/subject/mymsqrtx.html