Kotlin - 当CoroutineScope中发生Exception的各种状况

先谈谈于coroutine中发生exception的时候会有什么事情发生,先来看看一般的状况

import kotlinx.coroutines.*suspend fun getUsers(): List<String> {    delay(100L)    println("getUsers()")    return listOf("Alice","John")}suspend fun getMoreUsers(): List<String> {    delay(100L)    throw java.io.IOException("Hello")    return listOf("Tom","Jeff")}fun main() = runBlocking {        try {                val usersDeferred = async { getUsers() }        val moreUsersDeferred = async { getMoreUsers() }        val users = usersDeferred.await()        val moreUsers = moreUsersDeferred.await()    } catch (exception: java.io.IOException) {       println("execption...")    }}

可以看到结果,即使进行了try catch,还是会导致appCrash,也就是在coroutine 中的exception其实会引发CancellationException

getUsers()execption...Exception in thread "main" java.io.IOException: Hello at FileKt.getMoreUsers (File.kt:11)  at FileKt$getMoreUsers$1.invokeSuspend (File.kt:-1)  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33) 

透过corotuineScope builder 来执行

接着我们透过在coroutine的extension有提供一个builder corotuineScope

特色

可以抓捕内部coroutine的exception会马上取消剩下的coroutine

他的source code

public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R {    contract {        callsInPlace(block, InvocationKind.EXACTLY_ONCE)    }    return suspendCoroutineUninterceptedOrReturn { uCont ->        val coroutine = ScopeCoroutine(uCont.context, uCont)        coroutine.startUndispatchedOrReturn(coroutine, block)    }}

範例:

import kotlinx.coroutines.*suspend fun getUsers(): List<String> {    delay(100L)    println("getUsers()")    return listOf("Alice","John")}suspend fun getMoreUsers(): List<String> {    delay(100L)    throw java.io.IOException("Hello")    return listOf("Tom","Jeff")}suspend fun runHello() {    delay(300L)    println("runHello still alive...")}fun main() = runBlocking {        try {                coroutineScope {            val usersDeferred = async { getUsers() }            val moreUsersDeferred = async { getMoreUsers() }            val users = usersDeferred.await()            runHello()        }    } catch (exception: java.io.IOException) {       println("execption...")    }}

也就是加上coroutineScope后,他会有抓捕从coroutine抛出exception的能力,也会得到这样的log,如同我们平常执行regular function call的流程

getUsers()execption...

这样应该就也比较符合我们期待,也不会导致app Crash


那supervisorScope 又是什么?

特色

会捕抓exception会不理,也不会crash发生exceptoin的coroutine不会被处理,但剩下的coroutine会继续执行

看一下source code

public suspend fun <R> supervisorScope(block: suspend CoroutineScope.() -> R): R {    contract {        callsInPlace(block, InvocationKind.EXACTLY_ONCE)    }    return suspendCoroutineUninterceptedOrReturn { uCont ->        val coroutine = SupervisorCoroutine(uCont.context, uCont)        coroutine.startUndispatchedOrReturn(coroutine, block)    }}

如同下方的runHello即使上方遇到crash,他还是会继续执行

import kotlinx.coroutines.*suspend fun getUsers(): List<String> {    delay(100L)    println("getUsers()")    return listOf("Alice","John")}suspend fun getMoreUsers(): List<String> {    delay(100L)    throw java.io.IOException("Hello")    return listOf("Tom","Jeff")}suspend fun runHello() {    delay(100L)    println("runHello still alive...")}fun main() = runBlocking {        try {                supervisorScope {            val usersDeferred = async { getUsers() }            val moreUsersDeferred = async { getMoreUsers() }            val users = usersDeferred.await()            runHello()                    }    } catch (exception: java.io.IOException) {       println("execption...")    }}

因此这两种有两个不同的使用场景跟应用,所以很简单的端看你自己的应用场景是怎么使用,总结一下

不加scope - 即使有try catch,也不会被捕抓,app 会crash
corotuineScope - 会抓exception,会产生try-catch,同时停止其他corotuine
supervisorScope - 会抓exception,不会产生try-catch,但不会停止其他的corotuine

参考

https://waynestalk.com/kotlin-coroutine-tutorial/

https://amitshekhar.me/blog/kotlin-coroutines

https://kotlinlang.org/docs/exception-handling.html#exception-propagation


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章