是滴,这里只是一种可能,我们现在回到调用continuation
的地方,这里的continuation
在前面通过DispatchedContinuation
得到的,而实际上DispatchedContinuation
是个BaseContinuationImpl
对象(这里不扩展它是怎么来的,不然又得从头去找它的来源)。
val delegate = delegate as DispatchedContinuation
val continuation = delegate.continuation
internal abstract class BaseContinuationImpl(
public val completion: Continuation
) : Continuation
public final override fun resumeWith(result: Result
var current = this
var param = result
while (true) {
probeCoroutineResumed(current)
with(current) {
val completion = completion!! // fail fast when trying to resume continuation
val outcome: Result
try {
val outcome = invokeSuspend(param)
if (outcome === COROUTINE_SUSPENDED) return
Result.success(outcome)
} catch (exception: Throwable) {
Result.failure(exception)
}
releaseIntercepted() // this state machine instance is terminating
if (completion is BaseContinuationImpl) {
current = completion
param = outcome
} else {
completion.resumeWith(outcome)
return
}
}
}
}
}
可以看到最终这里面invokeSuspend
才是真正调用我们协程的地方。最后也是通过Continuation
调用resumeWith
函数恢复协程的执行,同时返回Result
类型的结果。和我们上面说的是一样的,只是他们是在不同阶段。
那、那、那、那下面那个finally
它又是有啥用,我们都通过resumeWithException
把异常抛出去了,为啥下面又还有个handleFatalException
,这货又是干啥用的???
handleFatalException
主要是用来处理kotlinx.coroutines
库的异常,我们这里大致的了解下就行了。主要分为两种:
kotlinx.coroutines
库或编译器有错误,导致的内部错误问题。
ThreadContextElement
也就是协程上下文错误,这是因为我们提供了不正确的ThreadContextElement
实现,导致协程处于不一致状态。
public interface ThreadContextElement : CoroutineContext.Element {
public fun updateThreadContext(context: CoroutineContext): S
public fun restoreThreadContext(context: CoroutineContext, oldState: S)
}
我们看到handleFatalException
实际是调用了handleCoroutineException
方法。handleCoroutineException
是kotlinx.coroutines
库中的顶级函数
public fun handleFatalException(exception: Throwable?, finallyException: Throwable?) {
//省略…
handleCoroutineException(this.delegate.context, reason)
}
public fun handleCoroutineException(context: CoroutineContext, exception: Throwable) {
try {
context[CoroutineExceptionHandler]?.let {
it.handleException(context, exception)
return
}
} catch (t: Throwable) {
handleCoroutineExceptionImpl(context, handlerException(exception, t))
return
}
handleCoroutineExceptionImpl(context, exception)
}
我们看到handleCoroutineException
会先从协程上下文拿CoroutineExceptionHandler
,如果我们没有定义的CoroutineExceptionHandler
话,它将会调用handleCoroutineExceptionImpl
抛出一个uncaughtExceptionHandler
导致我们程序崩溃退出。
internal actual fun handleCoroutineExceptionImpl(context: CoroutineContext, exception: Throwable) {
for (handler in handlers) {
try {
handler.handleException(context, exception)
} catch (t: Throwable) {
val currentThread = Thread.currentThread()
currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, handlerException(exception, t))
}
}
val currentThread = Thread.currentThread()
currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
}
不知道各位是否理解了上面的流程,笔者最开始的时候也是被这里来来回回的。绕着晕乎乎的。如果没看懂的话,可以休息一下,揉揉眼睛,倒杯热水,再回过头捋一捋。
好滴,到此处为止。我们已经大概的了解kotlin协程中异常是如何抛出的,下面我们就不再不过多延伸。下面我们来说说异常的处理。
协程的异常处理
kotlin协程异常处理我们要分成两部分来看,通过上面的分解我们知道一种异常是通过resumeWithException
抛出的,还有一种异常是直接通过CoroutineExceptionHandler
抛出,那么我们现在就开始讲讲如何处理异常。
第一种:当然就是我们最常用的try..catch
大法啦,只要有异常崩溃我就先try..catch
下,先不管流程对不对,我先保住我的程序不能崩溃。
private fun testException(){
GlobalScope.launch{
launch(start = CoroutineStart.UNDISPATCHED) {
Log.d("${Thread.currentThread().name}", " 我要开始抛异常了")
try {
throw NullPointerException(“异常测试”)
} catch (e: Exception) {
e.printStackTrace()
}
}
Log.d("${Thread.currentThread().name}", “end”)
}
}
D/DefaultDispatcher-worker-1: 我要开始抛异常了
W/System.err: java.lang.NullPointerException: 异常测试
W/System.err: at com.carman.kotlin.coroutine.MainActivity$testException$1$1.invokeSuspend(MainActivity.kt:252)
W/System.err: at com.carman.kotlin.coroutine.MainActivity$testException$1$1.invoke(Unknown
//省略…
D/DefaultDispatcher-worker-1: end
诶嘿,这个时候我们程序没有崩溃,只是输出了警告日志而已。那如果遇到try..catch
搞不定的怎么办,或者遗漏了需要try..catch
的位置怎么办。比如:
private fun testException(){
var a:MutableList = mutableListOf(1,2,3)
GlobalScope.launch{
launch {
Log.d("${Thread.currentThread().name}",“我要开始抛异常了” )
try {
launch{
Log.d(" T h r e a d . c u r r e n t T h r e a d ( ) . n a m e " , " {Thread.currentThread().name}", " Thread.currentThread().name"