协程异常、取消、失败处理

协程异常捕获方式:CoroutineExceptionHandler

CoroutineExceptionHandler是CoroutineContext的子类,可以用“+”进行连接,传入CoroutineScope或者launch函数(只能放入顶层协程)

 val handler= CoroutineExceptionHandler{ coroutineContext,ex->
    println(ex.message)
}
//传入Scope中
val scope = CoroutineScope(handler)
scope.launch{
    throw Exception("sss")
}
//传入launch函数中
GlobalScope.launch(handler){
    throw Exception("sss")
}

协程取消

创建协程的时候会返回一个Job对象,并且Job也是CoroutineContext的子类,这个对象提供了一个cancel方法来取消协程:

val job = GlobalScope.launch { 
    println("aaaa")
}
job.cancel()

当多个协程时可以这么写:

val job = Job()
val scope = CoroutineScope(job)
scope.launch {
    println("aaaa")
}
scope.launch {
    println("bbbb")
}
scope.launch {
    println("cccc")
}
job.cancel()

另外ktx的LifeCycler库已经封装了协程的取消操作,只需要用这个库的scope来launch协程就可以不用自己手动取消协程

协程失败时的行为:SuperVisorJob

coroutine_fail.png

协程的失败不是指程序崩溃,崩溃的情况下就谈不上失败了。协程失败的前提是将程序的异常捕获住,才能谈协程的失败。当一个协程失败时,会将失败事件冒泡到上一层,就是上图中的parent层,然后parent层会调用所有子协程的cancel()方法,将所有的子协程结束掉,然后调用自己的cancel()方法,接着继续往上冒泡,直至到顶层协程。一个协程一旦被取消之后,就不能再进行launch()。这个时候就需要使用SuperVisorJob来解决这个问题。SuperVisorJob本质上也是Job,只不过它有一个额外的行为:如果一个子协程失败时,SupervisorJob 不会对子协程或者它自己做任何其他的处理,你自己失败就可以了,这个是 SupervisorJob 它的作用。

supervisorjob.png

用法:

将协程的Job换成SupervisorJob

val handler = CoroutineExceptionHandler { _, ex ->
    ex.message?.let { Log.e(TAG, it) }
}
val scope = CoroutineScope(SupervisorJob() + handler)
scope.launch {
    Log.e(TAG, "第一个协程launch")
    throw RuntimeException()
}
scope.launch {
    Log.e(TAG, "第二个协程launch")
    delay(1000)
    Log.e(TAG, "第二个协程结束")
}
//执行结果
2020-10-29 16:00:26.860 7125-7220/com.example.taskdemo2 E/Coroutine: 第一个协程launch
2020-10-29 16:00:26.863 7125-7221/com.example.taskdemo2 E/Coroutine: 第二个协程launch
2020-10-29 16:00:27.881 7125-7221/com.example.taskdemo2 E/Coroutine: 第二个协程结束

这样两个协程之间就不会互相影响,再看如下情况:

scope.launch {
        launch {
            throw RuntimeException()
        }
        launch {
            
        }
    }

内部两个协程的不会继承父协程的SupervisorJob,因为协程的结构化中有一个规则: when the parent of a coroutine is another coroutine, the parent Job will always be of type Job(当协程的父级是另一个协程时,父级Job的类型始终为Job),所以它们的上下文依旧是最普通的Job,所有会相互影响,只需要在外层包上一层supervisorScope函数即可:

scope.launch {
        supervisorScope {
            launch {
                throw RuntimeException()
            }
            launch {

            } 
        }
    }

注意点:SupervisorJob()是为顶层协程设计的

参考自:https://medium.com/androiddevelopers/coroutines-first-things-first-e6187bf3bb21

你可能感兴趣的:(协程异常、取消、失败处理)