协程的异常处理 协程上下文 协程的异常捕获

 一:协程上下文CoroutineContext

协程上下文CoroutineContext 主要用来定义协程行为的元素

 协程CoroutineContext上下文组成公式  =  Job+默认值 +  继承的CoroutineContext  +参数

 Job: 控制协程的生命周期

默认值 = Dispatchers.Default(默认的CoroutineDispatcher 协程分器)+CoroutineName("coroutine")(协程的名称,可以自定义协程名称,调试的时候很有用)

继承的协程上下文CoroutineContext  = CoroutineScope或者其父协程的上下文CoroutineContext 

参数  =CoroutineExceptionHandler(处理未被捕获的异常)

val coroutineExceptionHandler  = CoroutineExceptionHandler(handler={_,exception->
    LogUtil.e("coroutineContext: 协程上下文异常啦: ${exception.message} ...")
})
val scope  = CoroutineScope(Job()+Dispatchers.Main+CoroutineName("我的协程CoroutineScope")+coroutineExceptionHandler)

 runBlocking {
            val coroutineExceptionHandler  = CoroutineExceptionHandler(handler={_,exception->
                LogUtil.e("coroutineContext: 协程上下文异常啦: ${exception.message} ...")
            })
            val scope  = CoroutineScope(Job()+Dispatchers.Main+CoroutineName("我的协程CoroutineScope")+coroutineExceptionHandler)
            val  job  =  scope.launch(Dispatchers.IO) {
               LogUtil.e("${coroutineContext[Job]}  ${Thread.currentThread().name}")
               val result = async {
                   LogUtil.e("${coroutineContext[Job]}  ${Thread.currentThread().name}")
                   "OK"
               }
               result.await()
              }

            job.join()
        }

SupervisorJob() 与 supervisorScope的区别
   SupervisorJob():
    val supervisor = CoroutineScope(SupervisorJob())
     一个子协程的运行失败不会影响其他的子协程的运行,也不会将异常传递给它的父级,
                 他会让子协程自己处理异常。
   supervisorScope:当作业自身执行失败的时候,其所有子协程都会被全部取消

   

 runBlocking {
            val supervisor = CoroutineScope(SupervisorJob())
            val job1 = supervisor.launch {
                delay(100)
                LogUtil.e("job1 was  executed")
                throw IllegalArgumentException()
            }
            val job2 = supervisor.launch {
                try {
                    delay(Long.MAX_VALUE)
                } finally {
                    LogUtil.e("job2  was   executed.")
                }
            }

             delay(200)
             supervisor.cancel() //这个意识是 取消这个协程的作用域  那么该协程作用域下的所有的子协程都会取消
             joinAll(job1, job2)  //输出结果:只输出 job1 was  executed  应为使用的是 SupervisorJob()
        }

三:协程的异常的捕获

1.协程的异常要想被捕获,必须是被协程所抛出的(只能使用launch有效能捕获到  async 无效不能捕获到),

2.在协程作用域CoroutinesScope 的上下文中

 

 private fun coroutineContextTest() {
        runBlocking {
            val coroutineExceptionHandler  = CoroutineExceptionHandler(handler={_,exception->
                LogUtil.e("coroutineContext: 协程上下文异常啦: ${exception.message} ...")
            })
            val scope  = CoroutineScope(Job()+Dispatchers.Main+CoroutineName("我的协程CoroutineScope")+coroutineExceptionHandler)
            val  job  =  scope.launch(Dispatchers.IO+coroutineExceptionHandler) {
               LogUtil.e("${coroutineContext[Job]}  ${Thread.currentThread().name}")
               val result = async(coroutineExceptionHandler) {
                   LogUtil.e("${coroutineContext[Job]}  ${Thread.currentThread().name}")
                   "OK"
               }
               result.await()
              }

            job.join()
        }


    }
  private fun coroutineExceptionHandlerTest1() {
       runBlocking {
           val coroutineExceptionHandler  = CoroutineExceptionHandler(handler={_,exception->
               LogUtil.e("coroutineContext: 捕获协程上下文异常: $exception ...")
           })

           //该协程的异常抛出  launch能够被捕获到
           val job = GlobalScope.launch(coroutineExceptionHandler) {
               throw AssertionError()
           }

           //该协程的异常抛出  async 不能够被捕获到
           val deferred = GlobalScope.async(coroutineExceptionHandler) {
               throw ArithmeticException()
           }
           job.join()
           deferred.await()
          }
    }
 private fun coroutineExceptionHandlerTest2() {
       runBlocking {
           val coroutineExceptionHandler  = CoroutineExceptionHandler(handler={_,exception->
               LogUtil.e("coroutineContext: 捕获协程上下文异常: $exception ...")
           })

           val scope = CoroutineScope(Job())
           val job = scope.launch(coroutineExceptionHandler) {
               launch {
                   throw IllegalArgumentException() //能够捕捉到协程异常  launch抛出的异常  父协程就能够捕获到该异常
               }
           }
           job.join()

       }

    }

private fun coroutineExceptionHandlerTest3() {
        runBlocking {
            val coroutineExceptionHandler  = CoroutineExceptionHandler(handler={_,exception->
                LogUtil.e("coroutineContext: 捕获协程上下文异常: $exception ...")
            })

            val scope = CoroutineScope(Job())
            val job = scope.launch {
                launch (coroutineExceptionHandler){
                    throw IllegalArgumentException() //不能够捕捉到协程异常   不能放到内部的 launch捕获  否则捕获不到
                }
            }
            job.join()

        }

    }

 

 协程的异常捕获防止APP闪退案例:

 

    val coroutineExceptionHandler  = CoroutineExceptionHandler(handler={_,exception->
            LogUtil.e("coroutineContext: 捕获协程上下文异常: $exception ...")
        })
        //铺货协程异常 APP不会闪退   输出异常信息: java.lang.StringIndexOutOfBoundsException: length=3; index=10 ...
        //如果不进行协程的异常捕获  APP就会闪退
        GlobalScope.launch(coroutineExceptionHandler) {
                "abc".substring(10)
        }

Android中全局异常的捕获与处理

Android 全局异常捕获 输出异常信息  但是程序还是会崩溃  可以收集异常信息上报 就知道程序哪里有问题

   

 //输出  协程全局异常捕获信息: java.lang.StringIndexOutOfBoundsException: length=3; index=10
    private fun coroutineExceptionHandlerTest5(){
        GlobalScope.launch{
            "abc".substring(10)
        }
    }

 如何配置Android 全局异常捕获类呢?

  第一步:创建GlobalCoroutineExceptionHandler 继承 CoroutineExceptionHandler

class GlobalCoroutineExceptionHandler : CoroutineExceptionHandler {

    override val key = CoroutineExceptionHandler

    override fun handleException(context: CoroutineContext, exception: Throwable) {
       LogUtil.e("coroutineExceptionHandler","协程全局异常捕获信息: ${exception.suppressed.contentToString()}") //输出多个异常信息数组
    }
}

 第二步: 在项目的根目录创建META-INF.services文件,在改文件里引用GlobalCoroutineExceptionHandler 的路径

com.dongnaoedu.kotlincoroutineexception.GlobalCoroutineExceptionHandler

所以协程里一定要加上捕获异常的代码,当你的协程发生了异常后 ,才能被捕获到到底是哪出现了问题

 协程的取消与异常

当协程取消时,会抛出CancellationException取消异常,这个取消异常CancellationException会被静默处理掉,我们不用管他,不会给项目造成影响,

当子协程取消时,这个取消CancellationException异常 不会往上抛给父协程的,不会影响不会取消父协程。

但是如果遇到了其他的异常,那么该子协程就会把该异常往上抛给父协程,

使父协程进行取消,父协程先要把其所有子协程都取消后,该异常才会被父协程处理。

 

runBlocking<Unit> {
        val handler = CoroutineExceptionHandler { _, exception ->
            println("Caught $exception")
        }
          //等所有的子协程取消后,父协程才能处理异常
        val job = GlobalScope.launch(handler) {
            //第一个子协程取消
            launch {
                try {
                    delay(Long.MAX_VALUE)
                } finally {
                    withContext(NonCancellable) {
                        println("Children are cancelled, but exception is not handled until all children terminate")
                        delay(100)
                        println("The first child finished its non cancellable block")
                    }
                }
            }
          //第二个子协程先抛异常
            launch {
                delay(10)
                println("Second child throws an exception")
                throw ArithmeticException()
            }
        }
        job.join()
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值