Kotlin 协程协程异常处理 ③ ( 协程异常处理器 CoroutineExceptionHandler 捕获异常 | 验证 CoroutineScope 协程的异常捕捉示例 )

Posted 韩曙亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin 协程协程异常处理 ③ ( 协程异常处理器 CoroutineExceptionHandler 捕获异常 | 验证 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, StandaloneCoroutineCancelling@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 ComponentInfokim.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, StandaloneCoroutineCancelling@bc6a601, Dispatchers.Default]
                 异常内容 java.lang.IllegalArgumentException
15:03:27.688  D  Skia GL Pipeline

以上是关于Kotlin 协程协程异常处理 ③ ( 协程异常处理器 CoroutineExceptionHandler 捕获异常 | 验证 CoroutineScope 协程的异常捕捉示例 )的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin 协程协程异常处理 ① ( 根协程异常处理 | 自动传播异常 | 在协程体捕获异常 | 向用户暴露异常 | 在 await 处捕获异常 | 非根协程异常处理 | 异常传播特性 )

Kotlin 协程协程异常处理 ① ( 根协程异常处理 | 自动传播异常 | 在协程体捕获异常 | 向用户暴露异常 | 在 await 处捕获异常 | 非根协程异常处理 | 异常传播特性 )

Kotlin 协程协程异常处理 ② ( SupervisorJob 协程 | supervisorScope 协程作用域构建器函数 )

Kotlin 协程协程异常处理 ② ( SupervisorJob 协程 | supervisorScope 协程作用域构建器函数 )

Kotlin 协程协程异常处理 ⑤ ( 异常传播的特殊情况 | 取消子协程示例 | 子协程抛出异常后父协程处理异常时机示例 | 异常聚合 | 多个子协程抛出的异常会聚合到第一个异常中 )

Kotlin 协程协程异常处理 ⑤ ( 异常传播的特殊情况 | 取消子协程示例 | 子协程抛出异常后父协程处理异常时机示例 | 异常聚合 | 多个子协程抛出的异常会聚合到第一个异常中 )