史上最详Android版kotlin协程入门进阶实战(三),Android多线程断点续传

是滴,这里只是一种可能,我们现在回到调用continuation的地方,这里的continuation在前面通过DispatchedContinuation得到的,而实际上DispatchedContinuation是个BaseContinuationImpl对象(这里不扩展它是怎么来的,不然又得从头去找它的来源)。

val delegate = delegate as DispatchedContinuation

val continuation = delegate.continuation

internal abstract class BaseContinuationImpl(

public val completion: Continuation<Any?>?

) : Continuation<Any?>, CoroutineStackFrame, Serializable {

public final override fun resumeWith(result: Result<Any?>) {

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<Any?> =

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<T>类型的结果。和我们上面说的是一样的,只是他们是在不同阶段。

那、那、那、那下面那个finally它又是有啥用,我们都通过resumeWithException把异常抛出去了,为啥下面又还有个handleFatalException,这货又是干啥用的???

handleFatalException主要是用来处理kotlinx.coroutines库的异常,我们这里大致的了解下就行了。主要分为两种:

  1. kotlinx.coroutines库或编译器有错误,导致的内部错误问题。

  2. ThreadContextElement也就是协程上下文错误,这是因为我们提供了不正确的ThreadContextElement实现,导致协程处于不一致状态。

public interface ThreadContextElement : CoroutineContext.Element {

public fun updateThreadContext(context: CoroutineContext): S

public fun restoreThreadContext(context: CoroutineContext, oldState: S)

}

我们看到handleFatalException实际是调用了handleCoroutineException方法。handleCoroutineExceptionkotlinx.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)

}

不知道各位是否理解了上面的流程,笔者最开始的时候也是被这里来来回回的。绕着晕乎乎的。如果没看懂的话,可以休息一下,揉揉眼睛,倒杯热水,再回过头捋一捋。

image.png

好滴,到此处为止。我们已经大概的了解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"

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值