先谈谈于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 会crashcorotuineScope
- 会抓exception,会产生try-catch,同时停止其他corotuinesupervisorScope
- 会抓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