在前几篇博客示例中 , 协程中 如果出现异常 , 没有进行捕获 , 则程序直接崩溃 , 这种情况下需要进行 异常的捕获 以 避免 Android 应用程序的崩溃 ;
示例代码 :
package kim.hsl.coroutine
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch {
Log.i(TAG, "验证协程中抛出异常")
throw IllegalArgumentException()
}
}
}
执行结果 : 在协程中抛出了异常 , 应用直接退出 ;
15:46:00.444 I 验证协程中抛出异常
15:46:00.486 D Skia GL Pipeline
15:46:00.504 E FATAL EXCEPTION: DefaultDispatcher-worker-1
Process: kim.hsl.coroutine, PID: 26587
java.lang.IllegalArgumentException
at kim.hsl.coroutine.MainActivity$onCreate$1.invokeSuspend(MainActivity.kt:18)
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)
15:46:00.511 W Force finishing activity kim.hsl.coroutine/.MainActivity
15:46:00.528 I Sending signal. PID: 26587 SIG: 9
---------------------------- PROCESS ENDED (26587) for package kim.hsl.coroutine ----------------------------
在 Android 程序中 , 可以使用 协程异常处理器 CoroutineExceptionHandler 捕获异常 , 将其实例对象传递给 launch 协程构建器 作为参数即可 ;
该参数作为 协程上下文 的 协程异常处理器 CoroutineExceptionHandler 元素 ;
代码示例 :
package kim.hsl.coroutine
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 创建 协程异常处理器 CoroutineExceptionHandler
val coroutineExceptionHandler = CoroutineExceptionHandler {
coroutineContext, throwable ->
Log.i(TAG, "CoroutineExceptionHandler 中处理异常 " +
"\n协程上下文 ${coroutineContext}" +
"\n异常内容 ${throwable}")
}
GlobalScope.launch {
Log.i(TAG, "验证协程中抛出异常")
throw IllegalArgumentException()
}
}
}
执行结果 : 协程异常处理器 CoroutineExceptionHandler 捕获到了异常 ;
15:47:54.749 I 验证协程中抛出异常
15:47:54.754 I CoroutineExceptionHandler 中处理异常
协程上下文 [kim.hsl.coroutine.MainActivity$onCreate$$inlined$CoroutineExceptionHandler$1@bc6a601, StandaloneCoroutine{Cancelling}@fef2ca6, Dispatchers.Default]
异常内容 java.lang.IllegalArgumentException
Android 中的 全局异常处理器 , 可以 获取 所有的 协程 中产生的 没有被捕获的异常 ;
全局异常处理器使用步骤如下 :
① 在 app/main/ 目录下创建 resources 目录 , 在 resources 目录下创建 META-INF 目录 ,
② 在 META-INF 目录下创建 services 目录 ,
③ 在 app/main/resources/META-INF/services 目录下 , 创建 名称为 kotlinx.coroutines.CoroutineExceptionHandler 的文件 ;
④ 创建 协程的 全局异常处理器 MyCoroutineExceptionHandler 自定义类 , 需要 实现 CoroutineExceptionHandler 接口 ; 并覆盖接口中的 val key
成员变量 和 fun handleException(context: CoroutineContext, exception: Throwable)
成员方法 ;
package kim.hsl.coroutine
import android.util.Log
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlin.coroutines.CoroutineContext
class MyCoroutineExceptionHandler : CoroutineExceptionHandler {
val TAG = "MyCoroutineExceptionHandler"
override val key = CoroutineExceptionHandler
override fun handleException(context: CoroutineContext, exception: Throwable) {
Log.i(TAG, "在 MyCoroutineExceptionHandler 全局异常处理器 中处理未捕获异常 " +
"\n协程上下文 ${context}" +
"\n抛出异常 ${exception}")
}
}
⑤ 在 app/main/resources/META-INF/services/kotlinx.coroutines.CoroutineExceptionHandler 文件中配置 协程的 全局异常处理器 MyCoroutineExceptionHandler 自定义类 的全类名 kim.hsl.coroutine.MyCoroutineExceptionHandler
, 如下图所示 :
⑥ 在 Activity 中实现一个 抛出异常的协程 :
package kim.hsl.coroutine
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch() {
Log.i(TAG, "验证协程中抛出异常")
throw IllegalArgumentException()
}
}
}
⑦ 执行上述应用 , 会抛出异常 , 协程中也不进行异常处理 , 此时执行结果如下 :
16:30:53.537 I 验证协程中抛出异常
16:30:53.578 D Skia GL Pipeline
16:30:53.590 I 在 MyCoroutineExceptionHandler 全局异常处理器 中处理未捕获异常
协程上下文 [StandaloneCoroutine{Cancelling}@8252a7e, Dispatchers.Default]
抛出异常 java.lang.IllegalArgumentException
16:30:53.593 E FATAL EXCEPTION: DefaultDispatcher-worker-1
Process: kim.hsl.coroutine, PID: 8808
java.lang.IllegalArgumentException
at kim.hsl.coroutine.MainActivity$onCreate$1.invokeSuspend(MainActivity.kt:18)
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)
16:30:53.599 W Force finishing activity kim.hsl.coroutine/.MainActivity
16:30:53.608 I Sending signal. PID: 8808 SIG: 9
16:30:53.627 I Process kim.hsl.coroutine (pid 8808) has died: cch CRE
---------------------------- PROCESS ENDED (8808) for package kim.hsl.coroutine ----------------------------
在 MyCoroutineExceptionHandler 全局异常处理器 中处理未捕获异常 , 但是程序依然崩溃 , 可以在 全局异常处理器 中获取到异常信息 ;