前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Kotlin 协程】协程异常处理 ③ ( 协程异常处理器 CoroutineExceptionHandler 捕获异常 | 验证 CoroutineScope 协程的异常捕捉示例 )

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

作者头像
韩曙亮
发布2023-03-30 18:25:35
1.2K0
发布2023-03-30 18:25:35
举报
文章被收录于专栏:韩曙亮的移动开发专栏

文章目录

一、协程异常处理器 CoroutineExceptionHandler 捕获异常


【Kotlin 协程】协程上下文 ( 协程上下文构成要素 | 指定协程上下文元素组合 | 协程上下文元素的继承关系 | 协程上下文元素的几种指定形式 | 默认 | 继承 | 自定义指定 ) 博客中 , 介绍了 协程上下文 CoroutineContext 组成要素 , 其中包含了 协程异常处理器 CoroutineExceptionHandler , 用于 在协程中捕获异常 ;

异常捕获 : 在协程中 , 使用 CoroutineExceptionHandler 对协程运行过程中产生的 异常 进行捕获 , 异常满足如下两个条件才会被捕 :

  • 异常捕获时机 : 协程 自动抛出 的异常 , 可以在协程内被捕获 ; 使用 launch 构建的协程 可以在协程中捕获异常 , 使用 async 构建的协程 在 await 处捕获异常 ;
  • 异常捕获位置 : 在 协程作用域 CoroutineScope 或者在 根协程 中 捕获 异常 ;

1、对比 launch 和 async 创建的协程的异常捕捉示例

代码示例 :

  • 使用 launch 构造的协程 , 可以使用 CoroutineExceptionHandler 捕获异常 ;
  • 使用 async 构造的协程 , 无法使用 CoroutineExceptionHandler 捕获异常 , 异常直接抛出 , 导致程序崩溃 ;
代码语言:javascript
复制
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 创建的协程中的异常直接抛出导致程序崩溃 ;

代码语言:javascript
复制
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()) 创建的协程的子协程 , 则异常不会被捕获到 ;

代码语言:javascript
复制
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 创建的协程的子协程中抛出异常 ;

代码语言:javascript
复制
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
在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-12-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一、协程异常处理器 CoroutineExceptionHandler 捕获异常
    • 1、对比 launch 和 async 创建的协程的异常捕捉示例
      • 2、验证 CoroutineScope 协程的异常捕捉示例
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档