一:协程上下文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()
}