欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Suspend函數(shù)與回調(diào)的互相轉(zhuǎn)換示例詳解

 更新時間:2023年01月15日 10:19:05   作者:Pika  
這篇文章主要為大家介紹了Suspend函數(shù)與回調(diào)的互相轉(zhuǎn)換示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

我們再來一期關(guān)于kotlin協(xié)程的故事,我們都知道在Coroutine沒有出來之前,我們對于異步結(jié)果的處理都是采用回調(diào)的方式進行,一方面回調(diào)層次過多的話,容易導(dǎo)致“回調(diào)地獄”,另一方法也比較難以維護。當然,我們并不是否定了回調(diào)本身,回調(diào)本身同時也是具備很多優(yōu)點的,比如符合代碼閱讀邏輯,同時回調(diào)本身也是比較可控的。這一期呢,我們就是來聊一下,如何把回調(diào)的寫法變成suspend函數(shù),同時如何把suspend函數(shù)變成回調(diào),從而讓我們更加了解kotlin協(xié)程背后的故事

回調(diào)變成suspend函數(shù)

來一個回調(diào)

我們以一個回調(diào)函數(shù)作為例子,當我們normalCallBack在一個子線程中做一些處理,比如耗時函數(shù),做完就會通過MyCallBack回調(diào)onCallBack,這里返回了一個Int類型,如下:

var myCallBack:MyCallBack?= null
interface MyCallBack{
    fun onCallBack(result: Int)
}
fun normalCallBack(){
    thread { 
        // 比如做一些事情
        myCallBack?.onCallBack(1)
    }
}

轉(zhuǎn)化為suspend函數(shù)

此時我們可以通過suspendCoroutine函數(shù),內(nèi)部其實是通過創(chuàng)建了一個SafeContinuation并放到了我們suspend函數(shù)本身(block本身)啟動了一個協(xié)程,我們之前在聊一聊Kotlin協(xié)程"低級"api 這篇文章介紹過

public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T {
    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
    return suspendCoroutineUninterceptedOrReturn { c: Continuation<T> ->
        val safe = SafeContinuation(c.intercepted())
        block(safe)
        safe.getOrThrow()
    }
}

這時候我們就可以直接寫為,從而將回調(diào)消除,變成了一個suspend函數(shù)。

suspend fun mySuspend() = suspendCoroutine<Int> {
    thread {
        // 比如做一些事情
        it.resume(1)
    }
}

當然,如果我們想要支持一下外部取消,比如當前頁面銷毀時,發(fā)起的網(wǎng)絡(luò)請求自然也就不需要再請求了,就可以通過suspendCancellableCoroutine創(chuàng)建,里面的Continuation對象就從SafeContinuation(見上文)變成了CancellableContinuation,變成了CancellableContinuation有一個invokeOnCancellation方便,支持在協(xié)程體被銷毀時的邏輯。

public suspend inline fun <T> suspendCancellableCoroutine(
    crossinline block: (CancellableContinuation<T>) -> Unit
): T =
    suspendCoroutineUninterceptedOrReturn { uCont ->
        val cancellable = CancellableContinuationImpl(uCont.intercepted(), resumeMode = MODE_CANCELLABLE)
        /*
         * For non-atomic cancellation we setup parent-child relationship immediately
         * in case when `block` blocks the current thread (e.g. Rx2 with trampoline scheduler), but
         * properly supports cancellation.
         */
        cancellable.initCancellability()
        block(cancellable)
        cancellable.getResult()
    }

此時我們就可以寫出以下代碼

suspend fun mySuspend2() = suspendCancellableCoroutine<Int> {
    thread {
        // 比如做一些事情
        it.resume(1)
    }
    it.invokeOnCancellation { 
        // 取消邏輯
    }
}

suspend函數(shù)變成回調(diào)

見到了回調(diào)如何變成suspend函數(shù),那么我們反過來呢?有沒有辦法?當然有啦!當時suspend函數(shù)中有很多種區(qū)分,我們一一區(qū)分一下

//直接返回的suspend函數(shù)
suspend fun myNoSuspendFunc():Int{
    return 1
}
//調(diào)用suspendCoroutine后直接resume的suspend函數(shù)
suspend fun myNoSuspendFunc() = suspendCoroutine<Int> {
        continuation ->
    continuation.resume(1)
}
//調(diào)用suspendCoroutine后異步執(zhí)行的suspend函數(shù)(這里異步可以是單線程也可以是多線程,跟線程本身無關(guān),只要是異步就會觸發(fā)掛起)
suspend fun myRealSuspendFunc() = suspendCoroutine<Int> {
    thread {
        Thread.sleep(300)
        it.resume(2)
    }

那么我們來想一下,這里真正發(fā)起掛起的函數(shù)是哪個?通過代碼其實我們可以猜到,真正掛起的函數(shù)只有最后一個myRealSuspendFunc,其他都不是真正的掛起,這里的掛起是什么意思呢?我們從協(xié)程的狀態(tài)就可以知道,當前處于CoroutineSingletons.COROUTINE_SUSPENDED時,就是掛起狀態(tài)。我們回歸一下,一個suspend函數(shù)有哪幾種情況

這里的1,2,3就分別對應(yīng)著上文demo中的例子

  • 直接返回結(jié)果,不需要進入狀態(tài)機判斷,因為本身就沒有啟動協(xié)程
  • 進入了協(xié)程,但是不需要進行SUSPEND狀態(tài)就已經(jīng)有了結(jié)果,所以直接返回了結(jié)果
  • 進入了SUSPEND狀態(tài),之后才能獲取結(jié)果

這里我們就不貼出來源碼了,感興趣可自己看Coroutine的實現(xiàn),這里我們要明確一個概念,一個Suspend函數(shù)的運行機制,其實并不依靠了協(xié)程本身。

對應(yīng)代碼表現(xiàn)就是,這個函數(shù)的返回結(jié)果可能就是直接返回結(jié)果本身,另一種就是通過回調(diào)本身通知外部(這里我們還會以例子說明)

suspend函數(shù)轉(zhuǎn)換為回調(diào)

這里有兩種情況,我們分別以kotlin代碼跟java代碼表示:

kotlin代碼

由于kotlin可以直接通過suspend的擴展函數(shù)startCoroutine啟動一個協(xié)程,

fun myRealSuspendCallBack(){
    ::myRealSuspendFunc.startCoroutine(object :Continuation<Int>{
       當前環(huán)境
        override val context: CoroutineContext
            get() = Dispatchers.IO
        結(jié)果
        override fun resumeWith(result: Result<Int>) {
            if(result.isSuccess){
                myCallBack?.onCallBack(result.getOrDefault(0))
            }
        }
    })
}

其中Result就是一個內(nèi)聯(lián)類,屬于kotlin編譯器添加的裝飾類,在這里我們無論是1,2,3的情況,都可以在resumeWith 中獲取到結(jié)果,在這里通過callback回調(diào)即可

@JvmInline
public value class Result<out T> @PublishedApi internal constructor(
    @PublishedApi
    internal val value: Any?
) : Serializable {

Java代碼

這里我們更正一個誤區(qū),就是suspend函數(shù)只能在kotlin中使用/Coroutine協(xié)程只能在kotlin中使用,這個其實是錯誤的,java代碼也能夠調(diào)起協(xié)程,只不過麻煩了一點,至少官方是沒有禁止的。 比如我們需要調(diào)用startCoroutine,可直接調(diào)用

ContinuationKt.startCoroutine();

當然,我們也能夠直接調(diào)用suspend函數(shù)

Object result = CallBack.INSTANCE.myRealSuspendFunc(new Continuation<Integer>() {
    @NonNull
    @Override
    public CoroutineContext getContext() {
        //這里啟動的環(huán)境其實協(xié)程沒有用到,讀者們可以思考一下為什么!這里就當一個謎題啦!可以在評論區(qū)說出你的想法(我會在評論區(qū)解答)
        return (CoroutineContext) Dispatchers.getIO();
        //return EmptyCoroutineContext.INSTANCE;
    }
    @Override
    public void resumeWith(@NonNull Object o) {
        情況3
        Log.e("hello","resumeWith result is "+ o +" is main "+ (Looper.myLooper() == Looper.getMainLooper()));
          // 回調(diào)處理即可
          myCallBack?.onCallBack(result)
    }
});
if(result != IntrinsicsKt.getCOROUTINE_SUSPENDED()){
    情況1,2
    Log.e("hello","func result is "+ result);
      // 回調(diào)處理即可
      myCallBack?.onCallBack(result)
}

這里我們需要注意的是,這里java代碼比kotlin多了一個判斷,同時resumeWith的參數(shù)不再是Result,而是一個Object

if(result != IntrinsicsKt.getCOROUTINE_SUSPENDED()){
}

這里脫去了kotlin給我們添加的各種外殼,其實這就是真正的對于suspend結(jié)果的處理(只不過kotlin幫我們包了一層)

我們上文說過,suspend函數(shù)對應(yīng)的三種情況,這里的1,2都是直接返回結(jié)果的,因為沒有走到SUSPEND狀態(tài)(IntrinsicsKt.getCOROUTINE_SUSPENDED())這里需要讀者好好閱讀上文,因此 result != IntrinsicsKt.getCOROUTINE_SUSPENDED(),就會直接走到這里,我們就直接拿到了結(jié)果

if(result != IntrinsicsKt.getCOROUTINE_SUSPENDED()){
}

如果屬于情況3,那么這里的result就不再是一個結(jié)果,而是當前協(xié)程的狀態(tài)標記罷了,此時當協(xié)程完成執(zhí)行的時候(調(diào)用resume的時候),就會回調(diào)到resumeWith,這里的Object類型o才是經(jīng)過SUSPEND狀態(tài)的結(jié)果!

總結(jié)

經(jīng)過我們suspend跟回調(diào)的互相狀態(tài),能夠明白了suspend背后的邏輯與掛起的細節(jié),希望能幫到你

以上就是Suspend函數(shù)與回調(diào)的互相轉(zhuǎn)換示例詳解的詳細內(nèi)容,更多關(guān)于Suspend函數(shù)回調(diào)轉(zhuǎn)換的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論