协程学习(九)协程简单的使用之异常处理

在上一篇文章中有说过,协程中的异常最初是由下向上抛的,那么我可不可以在 中间层协程中捕获异常呢,下面看一下案例

kotlin 复制代码
@JvmStatic
fun main(array: Array<String>) {
    runBlocking {
        var error=CoroutineExceptionHandler { _, throwable ->
            println("Tsm Error:${throwable}")
        }
       var scope= CoroutineScope(SupervisorJob()+ Dispatchers.IO)
        scope.launch {
            launch {
                launch {
                    launch(error) {
                        launch {
                            throw NullPointerException("我错了")
                        }
                    }
                }
            }
        }
        delay(5*1000)
    }
}

在这里创建了一个新的 CoroutineScope ,指定io线程 与 阻止子协程异常而向导致其他兄弟协程异常退出,最终在最内层的子协程中跑了一个异常,并在倒数第二层的协程中传入了 CoroutineExceptionHandler 想要拦截异常

看一下结果

php 复制代码
Exception in thread "DefaultDispatcher-worker-2 @coroutine#6" java.lang.NullPointerException: 我错了
	at com.tsm.opencv.Tsm$main$1$1$1$1$1$1.invokeSuspend(Tsm.kt:23)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
	at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:100)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [CoroutineId(2), "coroutine#2":StandaloneCoroutine{Cancelling}@1397bbe2, Dispatchers.IO]

Process finished with exit code 0

发现这个异常还是引起了崩溃,说明这个异常最终只能抛给顶级父协程来处理,中间协程拦截不了

我们再来看一下 async 的表现

typescript 复制代码
@JvmStatic
fun main(array: Array<String>) {
    runBlocking {
        var error=CoroutineExceptionHandler { _, throwable ->
            println("Tsm Error:${throwable}")
        }
        var scope= CoroutineScope(SupervisorJob()+ Dispatchers.IO)

        scope.async{
            async {
                async {
                    async(error) {
                        async {
                            throw NullPointerException("我错了")
                        }
                    }
                }
            }
        }
        delay(5*1000)
    }
}
结果:
Process finished with exit code 0

虽然抛出了异常但是程序没有接受到这个异常,我们使用await 接受一下结果看一下

swift 复制代码
Exception in thread "main" java.lang.NullPointerException: 我错了
	at com.tsm.opencv.Tsm$main$1$1$1$1$1$1.invokeSuspend(Tsm.kt:44)
	at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt:46)
	at com.tsm.opencv.Tsm$main$1.invokeSuspend(Tsm.kt:49)
Caused by: java.lang.NullPointerException: 我错了
	at com.tsm.opencv.Tsm$main$1$1$1$1$1$1.invokeSuspend(Tsm.kt:44)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
	at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:100)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)

Process finished with exit code 1

可以看到我们使用await后异常就出现了,这说明 async 虽然也收到了这个异常,但是只有在await后,才会获取异常或者结果从而将他抛出

下面将异常交给顶级父协程来处理看一下结果

先看一下launch 的表现

kotlin 复制代码
@JvmStatic
fun main(array: Array<String>) {
    runBlocking {
        var error=CoroutineExceptionHandler { _, throwable ->
            println("Tsm Error:${throwable}")
        }
       var scope= CoroutineScope(SupervisorJob()+ Dispatchers.IO)
        scope.launch(error) {
            launch {
                launch {
                    launch{
                        launch {
                            throw NullPointerException("我错了")
                        }
                    }
                }
            }
        }
        delay(5*1000)
    }
}
结果:

Tsm Error:java.lang.NullPointerException: 我错了

Process finished with exit code 0

可以看到异常确实被捕获了

再来看看 async

less 复制代码
@JvmStatic
fun main(array: Array<String>) {
    runBlocking {
        var error = CoroutineExceptionHandler { _, throwable ->
            println("Tsm Error:${throwable}")
        }

        var scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
        var job=scope.async(error) {
            throw NullPointerException("我错了")
        }
        job.await()
        delay(1 * 1000)
    }
}
结果:
Exception in thread "main" java.lang.NullPointerException: 我错了
	at com.tsm.opencv.Tsm$main$1$job$1.invokeSuspend(Tsm.kt:42)
	at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt:46)
	at com.tsm.opencv.Tsm$main$1.invokeSuspend(Tsm.kt:44)
Caused by: java.lang.NullPointerException: 我错了
	at com.tsm.opencv.Tsm$main$1$job$1.invokeSuspend(Tsm.kt:42)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
	at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:100)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)

Process finished with exit code 1

可以发现在使用async 的过程中即使使用了 CoroutineExceptionHandler 还是抛出了异常,这个是为什么呢, 原因与我们在上面讲的 只有await 的时候才可以获取 这个异常是一样的, 是因为 async 方法只是创建一个协程,但是异常只有调用了await 才能获取,那么在实际开发中该如何处理这种情况呢

1: 在 async 中使用 try catch 捕获异常,保险起见 CoroutineExceptionHandler 还是要在顶级父协程加上的 方式如下

kotlin 复制代码
@JvmStatic
fun main(array: Array<String>) {
    runBlocking {
        var error = CoroutineExceptionHandler { _, throwable ->
            println("Tsm Error:${throwable}")
        }

        var scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
        var job=scope.async(error) {
            try {
                throw NullPointerException("我错了")
            } catch (e: Exception) {
            }
        }
        job.await()
        delay(1 * 1000)
    }
}

2: 为了保险起见在 async 中同样使用 CoroutineExceptionHandler,但是在 await 出try catch

kotlin 复制代码
@JvmStatic
fun main(array: Array<String>) {
    runBlocking {
        var error = CoroutineExceptionHandler { _, throwable ->
            println("Tsm Error:${throwable}")
        }

        var scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
        var job=scope.async(error) {
            throw NullPointerException("我错了")
        }
        job.runCatching { await() }.getOrElse {

        }
        delay(1 * 1000)
    }
}

3: 使用launch 作为顶级父协程 + CoroutineExceptionHandler

kotlin 复制代码
@JvmStatic
fun main(array: Array<String>) {
    runBlocking {
        var error = CoroutineExceptionHandler { _, throwable ->
            println("Tsm Error:${throwable}")
        }

        var scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
        scope.launch(error) {
            var job = async {
                throw NullPointerException("我错了")
            }
            job.await()
        }
        delay(1 * 1000)
    }
}

可以看到使用第三种方法更为的优雅但是具体选择就看个人的喜好了,

相关推荐
r***18642 分钟前
FlinkCDC实战:将 MySQL 数据同步至 ES
android·mysql·elasticsearch
p***62994 分钟前
mysql-connector-java 和 mysql-connector-j的区别
android·java·mysql
海天鹰18 分钟前
ACTION_PICK与ACTION_GET_CONTENT的区别
android
i***683226 分钟前
图文详述:MySQL的下载、安装、配置、使用
android·mysql·adb
6***v41732 分钟前
MySQL 时区参数 time_zone 详解
android·mysql·adb
王能34 分钟前
Android三阶贝塞尔曲线应用——根据指定点画出完美的贝塞尔
android·贝塞尔曲线·图表·顶点
c***421035 分钟前
MySQL-触发器(TRIGGER)
android·数据库·mysql
有位神秘人37 分钟前
Android视频播放方案
android·音视频
U***l83241 分钟前
MySql的慢查询(慢日志)
android·mysql·adb
2501_9159184141 分钟前
iOS 手机抓包软件怎么选?HTTPS 调试、TCP 数据流分析与多工具组合的完整实践
android·ios·智能手机·小程序·https·uni-app·iphone