学完状态机之后来看看他可以帮我们理解什么。
我们回头看看suspendCoroutineUninterceptedOrReturn获取挂起函数的注意点:

1.不要轻易在同一个调用挂起函数的栈帧中调回Continuation.resumeWithException或者Continuation.resume
2. 如果不是立即返回结果,请务必返回kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED

看一个例子

//com.example.studycoroutine.chapter.two.MyCoroutine.kt
class MyCoroutine() : Continuation<String> {
    override fun resumeWith(result: Result<String>) {


        logD("MyCoroutine 回调resumeWith 返回的结果 " + result.getOrNull())
    }

    override val context: CoroutineContext
        get() = kotlin.coroutines.EmptyCoroutineContext

}



/**
 * 
 */
suspend fun firstSuspend() = suspendCoroutineUninterceptedOrReturn<String> { continuation ->

    continuation.resume("hello ")
    logD("虽然协程返回了结果,但是我依然要打印")

    kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
}


fun defaultResumeIo() {

    val lambda = suspend {
        val firstSuspend = firstSuspend()
        logD("返回的结果是" + firstSuspend)
        firstSuspend
    }


    val myCoroutine = MyCoroutine()
    lambda.startCoroutine(myCoroutine)
}

输出:

[main] 返回的结果是hello 
[main] MyCoroutine 回调resumeWith 返回的结果 hello 
[main] 虽然协程返回了结果,但是我依然要打印

这很显然存在一个逻辑问题,重复进入状态机两次,第一次进入正常启动进入,第二次continuation.resume("hello ")调用后立即进入BaseContinuationImplresumeWith函数,然后显示的完成结果于是回调MyCoroutine。然后firstSuspend却没结束,接着打印一个log。然后返回kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED,此时BaseContinuationImpl判断结果是挂起标识,于是返回。虽然上述代码不会奔溃,但是很容易出现意向不到的问题。

来模拟一个奔溃吧

suspend fun firstSuspend() = suspendCoroutineUninterceptedOrReturn<String> { continuation ->

    continuation.resume("hello ")
    logD("虽然协程返回了结果,但是我依然要打印")
//    kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
    "你好返回"
}


fun defaultResumeIo() {

    val lambda = suspend {
        val firstSuspend = firstSuspend()
        logD("返回的结果是" + firstSuspend)
        firstSuspend
    }


    val myCoroutine = MyCoroutine()
    lambda.startCoroutine(myCoroutine)
}

输出:

2020-03-27 21:51:52.746 10797-10797/com.example.studycoroutine E/LOG: [main] 返回的结果是hello 
2020-03-27 21:51:52.746 10797-10797/com.example.studycoroutine E/LOG: [main] MyCoroutine 回调resumeWith 返回的结果 hello 
2020-03-27 21:51:52.746 10797-10797/com.example.studycoroutine E/LOG: [main] 虽然协程返回了结果,但是我依然要打印
2020-03-27 21:51:52.746 10797-10797/com.example.studycoroutine E/LOG: [main] 返回的结果是你好返回
2020-03-27 21:51:52.747 10797-10797/com.example.studycoroutine D/AndroidRuntime: Shutting down VM
2020-03-27 21:51:52.747 10797-10797/com.example.studycoroutine E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.studycoroutine, PID: 10797
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.studycoroutine/com.example.studycoroutine.MainActivity}: kotlin.KotlinNullPointerException
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6077)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
     Caused by: kotlin.KotlinNullPointerException
        at kotlin.coroutines.jvm.internal.ContinuationImpl.releaseIntercepted(ContinuationImpl.kt:118)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:39)
        at kotlin.coroutines.ContinuationKt.startCoroutine(Continuation.kt:114)
        at com.example.studycoroutine.chapter.seven.InterceptDemoKt.defaultResumeIo(InterceptDemo.kt:31)
        at com.example.studycoroutine.MainActivity.onCreate(MainActivity.kt:17)
        at android.app.Activity.performCreate(Activity.java:6662)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2599)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6077) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756) 

这回输出异常,还奔溃了。
MyCoroutine回调两次,最后引起空指针。
回调两次MyCoroutine是由于suspendCoroutineUninterceptedOrReturn最后返回的不为挂起函数的标识,导致BaseContinuationImpl重复判断。至于空指针就不用多说了大家学习拦截器的时候再看看 ,这里是由于调用releaseIntercepted内部释放资源导致

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐