文章目录
- 一、协程异常处理器 CoroutineExceptionHandler 捕获异常
- 1、对比 launch 和 async 创建的协程的异常捕捉示例
- 2、验证 CoroutineScope 协程的异常捕捉示例
一、协程异常处理器 CoroutineExceptionHandler 捕获异常
在 【Kotlin 协程】协程上下文 ( 协程上下文构成要素 | 指定协程上下文元素组合 | 协程上下文元素的继承关系 | 协程上下文元素的几种指定形式 | 默认 | 继承 | 自定义指定 ) 博客中 , 介绍了 协程上下文 CoroutineContext 组成要素 , 其中包含了 协程异常处理器 CoroutineExceptionHandler , 用于 在协程中捕获异常 ;
异常捕获 : 在协程中 , 使用 CoroutineExceptionHandler 对协程运行过程中产生的 异常 进行捕获
- 异常捕获时机 : 协程 自动抛出 的异常 , 可以在协程内被捕获 ; 使用 launch 构建的协程 可以在协程中捕获异常 , 使用 async 构建的协程 在 await 处捕获异常 ;
- 异常捕获位置 : 在 协程作用域 CoroutineScope 或者在 根协程 中
1、对比 launch 和 async 创建的协程的异常捕捉示例
代码示例 :
- 使用 launch 构造的协程 , 可以使用 CoroutineExceptionHandler 捕获异常 ;
- 使用 async 构造的协程 , 无法使用 CoroutineExceptionHandler 捕获异常 , 异常直接抛出 , 导致程序崩溃 ;
package kim.hsl.coroutine
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 将主线程包装成协程
runBlocking<Unit> {
// 创建 协程异常处理器 CoroutineExceptionHandler
val coroutineExceptionHandler = CoroutineExceptionHandler {
coroutineContext, throwable ->
Log.i(TAG, "CoroutineExceptionHandler 中处理异常 " +
"\n协程上下文 ${coroutineContext}" +
"\n异常内容 ${throwable}")
}
// 创建协程 , 传入 CoroutineExceptionHandler 作为协程上下文参数
val job = GlobalScope.launch(coroutineExceptionHandler) {
// 该异常会被捕获
throw AssertionError()
}
// 创建协程 , 传入 CoroutineExceptionHandler 作为协程上下文参数
val deferred = GlobalScope.async(coroutineExceptionHandler) {
// 该异常不会被捕获
throw ArithmeticException()
}
// 等待 job 执行完毕
job.join()
// 等待 deferred 执行完毕
deferred.await()
}
}
}
执行结果 : 捕获到了 launch 创建的协程中的异常 , 但是 async 创建的协程中的异常直接抛出导致程序崩溃 ;
14:35:22.587 I CoroutineExceptionHandler 中处理异常
协程上下文 [kim.hsl.coroutine.MainActivity$onCreate$1$invokeSuspend$$inlined$CoroutineExceptionHandler$1@f30fe8, StandaloneCoroutine{Cancelling}@bc6a601, Dispatchers.Default]
异常内容 java.lang.AssertionError
14:35:22.591 D Shutting down VM
14:35:22.595 E FATAL EXCEPTION: main
Process: kim.hsl.coroutine, PID: 30617
java.lang.RuntimeException: Unable to start activity ComponentInfo{kim.hsl.coroutine/kim.hsl.coroutine.MainActivity}: java.lang.ArithmeticException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2951)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3086)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6718)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.ArithmeticException
at kim.hsl.coroutine.MainActivity$onCreate$1$deferred$1.invokeSuspend(MainActivity.kt:33)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
14:35:22.605 W Force finishing activity kim.hsl.coroutine/.MainActivity
14:35:22.618 I Sending signal. PID: 30617 SIG: 9
---------------------------- PROCESS ENDED (30617) for package kim.hsl.coroutine ----------------------------
2、验证 CoroutineScope 协程的异常捕捉示例
在使用 CoroutineExceptionHandler 对协程运行过程中产生的 异常 进行捕获 时 , 异常捕获的位置 只能是 协程作用域 CoroutineScope 或者在 根协程 中
在上面的小节验证了 异常捕获位置 在根协程 中的情况 , 在本小节示例中 , 验证在 协程作用域 CoroutineScope 中捕获异常 ;
代码示例 : 在 协程作用域 中 , 使用 launch 协程构建器 创建协程 , 传入 CoroutineExceptionHandler 实例对象参数
下面代码中 创建协程作用域 时 , 使用的 CoroutineScope(Job())
进行创建 , 不是 SupervisorJob
, 因此 在子协程中抛出的异常 , 会传递给父协程 , 由父协程处理异常 , 父协程创建时使用的 val job = scope.launch(coroutineExceptionHandler)
代码 , 在协程构建器中传入了 协程异常处理器 , 因此该协程异常处理器 可捕获 子协程传递给父协程的异常 ;
异常处理器 coroutineExceptionHandler , 必须安装给 根协程 , 不能给内部协程安装 ; 如果将 coroutineExceptionHandler 设置给 CoroutineScope(Job())
创建的协程的子协程 , 则异常不会被捕获到 ;
package kim.hsl.coroutine
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 将主线程包装成协程
runBlocking<Unit> {
// 创建 协程异常处理器 CoroutineExceptionHandler
val coroutineExceptionHandler = CoroutineExceptionHandler {
coroutineContext, throwable ->
Log.i(TAG, "CoroutineExceptionHandler 中处理异常 " +
"\n协程上下文 ${coroutineContext}" +
"\n异常内容 ${throwable}")
}
// 验证 在 协程作用域 CoroutineScope 中的异常
// 可以使用 CoroutineExceptionHandler 捕获
val scope = CoroutineScope(Job())
val job = scope.launch(coroutineExceptionHandler) {
launch {
throw IllegalArgumentException()
}
}
// 等待 job 执行完毕
job.join()
}
}
}
执行结果 : 捕获到了在 CoroutineScope 创建的协程的子协程中抛出异常 ;
15:03:27.654 I CoroutineExceptionHandler 中处理异常
协程上下文 [kim.hsl.coroutine.MainActivity$onCreate$1$invokeSuspend$$inlined$CoroutineExceptionHandler$1@f30fe8, StandaloneCoroutine{Cancelling}@bc6a601, Dispatchers.Default]
异常内容 java.lang.IllegalArgumentException
15:03:27.688 D Skia GL Pipeline