最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Kotlin 協(xié)程與掛起函數(shù)及suspend關(guān)鍵字深入理解

 更新時間:2022年12月08日 12:00:29   作者:無糖可樂愛好者  
這篇文章主要為大家介紹了Kotlin 協(xié)程與掛起函數(shù)及suspend關(guān)鍵字深入理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

1.掛起函數(shù)

掛起函數(shù)在Kotlin協(xié)程中是一個比較重要的知識點,協(xié)程的非阻塞式、Channel、Flow等API都對它有充分的理解才能在學(xué)習(xí)時事半功倍。

已知的是Kotlin協(xié)程的特點是輕量和非阻塞, 單靠這兩個點就能說明Kotlin協(xié)程就的優(yōu)勢嗎,不一定。這里先提出一個結(jié)論:掛起函數(shù)是Kotlin協(xié)程的最大優(yōu)勢。 下面對于掛起函數(shù)的講解就圍繞這句話展開。

以獲取省市區(qū)Code為例,獲取區(qū)域Code的前提是要有城市Code,城市Code的前提是要有省份Code,請求結(jié)果通過CallBack返回。

public class RequestCode {
    public static void main(String[] args) {
        getProvincesCode(provincesCode -> {
            getCityCode(provincesCode, cityCode -> {
                getAreaCode(cityCode, areaCode -> {
                });
            });
        });
    }
    /**
     * 獲取省份Code
     *
     * @param callBack
     */
    private static void getProvincesCode(CallBack callBack) {
        callBack.onSuccess("100000");
    }
    /**
     * 獲取城市Code
     *
     * @param provincesCode
     * @param callBack
     */
    private static void getCityCode(String provincesCode, CallBack callBack) {
        callBack.onSuccess("100010");
    }
    /**
     * 獲取區(qū)域code
     *
     * @param cityCode
     * @param callBack
     */
    private static void getAreaCode(String cityCode, CallBack callBack) {
        callBack.onSuccess("100011");
    }
}

上面的代碼在開發(fā)中都遇到過,代碼可以優(yōu)化的更簡潔更易讀,這里主要是證明掛起函數(shù)的優(yōu)勢,就不做優(yōu)化了。

上面的代碼可以看到三層嵌套是比較復(fù)雜的,如果再加上獲取國家、區(qū)域街道的話嵌套層級會更深,這樣就會對可讀性、可擴展性、可維護性都有影響。那么用Kotlin這個代碼要怎么寫呢?

現(xiàn)在我用delay(1000L)替代CallBack模擬網(wǎng)絡(luò)請求

fun main() = runBlocking {
    val provincesCode = getProvincesCode()
    val cityCode = getCityCode(provincesCode)
    val areaCode = getAreaCode(cityCode)
}
/**
 * 獲取省份Code
 *
 */
private suspend fun getProvincesCode(): String {
    withContext(Dispatchers.IO) {
        delay(1000L)
    }
    return "100000"
}
/**
 * 獲取城市Code
 *
 * @param provincesCode
 */
private suspend fun getCityCode(provincesCode: String): String {
    withContext(Dispatchers.IO) {
        delay(1000L)
    }
    return "100010"
}
/**
 * 獲取區(qū)域code
 *
 * @param cityCode
 */
private suspend fun getAreaCode(cityCode: String): String {
    withContext(Dispatchers.IO) {
        delay(1000L)
    }
    return "100011"
}

Kotlin實現(xiàn)了同步方式完成異步請求,這種方式的實現(xiàn)要歸功于具體的函數(shù)的實現(xiàn),在這三個函數(shù)中可以發(fā)現(xiàn)他們的定義與普通函數(shù)的區(qū)別是多了一個suspend關(guān)鍵字,它的意思就是掛起。

再回頭看main函數(shù),前面加上了runBlocking也就是說建立了一個協(xié)程的作用域,這是因為suspend的出現(xiàn),因為suspend的作用就是掛起和恢復(fù),而掛起和恢復(fù)是需要上下文的,因此就需要定義一個作用域,這里選擇了runBlocking。

在函數(shù)中還有一個地方withContext(Dispatchers.IO)這主要是執(zhí)行線程的切換,除了IO線程以外還有Main、default等線程。

在Kotlin中掛起和恢復(fù)是成對出現(xiàn)的,因為既然會被掛起那肯定也會被恢復(fù),而協(xié)程的非阻塞也是因為掛起和恢復(fù)能力,那么掛起和恢復(fù)又是怎么樣的一個含義呢?

首先執(zhí)行getProvincesCode()這是一個掛起函數(shù),因為用了Dispatchers.IO切換到了IO線程因此這個等待時間就在IO線程執(zhí)行,當?shù)却龝r間結(jié)束后(CallBack回調(diào)結(jié)果)provincesCode收到返回的結(jié)果,這個函數(shù)中suspend掛起,main函數(shù)中收到結(jié)果就是恢復(fù)能力,

我們看一下debug日志:

  • 這是getProvincesCode的協(xié)程日志,可以獲取以下幾個信息:
  • 當前協(xié)程的名字是:coroutine#1
  • 協(xié)程運行在主線程:main
  • 任務(wù)被掛起

withContext(Dispatchers.IO)切換線程

任務(wù)切換到DefaultDispatcher-worker-1線程繼續(xù)執(zhí)行

  • return "?。?00000"回到主線程執(zhí)行

我們總結(jié)一下上面的日志:

  • mainDefaultDispatcher-worker-1的過程是掛起;
  • DefaultDispatcher-worker-1main的過程是恢復(fù);

那么掛起和恢復(fù)的含義也有非常明確了:

  • 掛起: 只是將程序執(zhí)行流程轉(zhuǎn)移到了其他線程,主線程并不會阻塞。我們知道Android中如果阻塞超過一定時間就會出現(xiàn)無響應(yīng),上面的代碼在運行過程中并不會導(dǎo)致無響應(yīng)的發(fā)生,在代碼執(zhí)行的過程中我們還可以做其他事情的,因為任務(wù)進入到子線程后主線程的狀態(tài)是空閑的。
  • 恢復(fù): 當子線程的任務(wù)執(zhí)行完畢后再回到主線程的過程就叫做恢復(fù)。

掛起和恢復(fù)的能力是掛起函數(shù)特有的能力,普通函數(shù)時不具備的,如果在一個普通函數(shù)中僅僅加上suspend關(guān)鍵字會發(fā)現(xiàn)其實并沒有什么用,編譯器會告知這種定義是多余的。

上面還提出了一個結(jié)論——掛起函數(shù)是Kotlin協(xié)程的最大優(yōu)勢, 首先因為函數(shù)的執(zhí)行過程中可以切換線程,其次函數(shù)的運行不會影響主線程導(dǎo)致主線程被阻塞。

2.深入理解suspend

上面掛起函數(shù),掛起函數(shù)主要就是主線程切換到子線程,這個過程又是如何實現(xiàn)的?

已知掛起函數(shù)是依靠關(guān)鍵字suspend,我們將帶有這個關(guān)鍵子的函數(shù)轉(zhuǎn)換成Java代碼進行分析:

private static final Object getProvincesCode(Continuation var0) {
    ...
    CoroutineContext var10000 = (CoroutineContext)Dispatchers.getIO();
    ...
    return "?。?00000";
}

代碼比較長,只保留了需要的地方。

  • getProvincesCode里面多了一個參數(shù)Continuation,意思是延續(xù),而suspend沒有了,那么Continuation又是什么
public interface Continuation<in T> {
    /**
     * 協(xié)程的上下文.
     */
    public val context: CoroutineContext
    /**
     * 恢復(fù)執(zhí)行相應(yīng)的協(xié)程,傳遞一個成功或失敗的[result]作為最后一個掛起點的返回值
     */
    public fun resumeWith(result: Result<T>)
}

Continuation的源碼發(fā)現(xiàn)一個問題,它跟我們開頭的CallBack是類似的,其中resumeWithonSucess的功能也是一樣的,區(qū)別在于Continuation是有一個泛型的參數(shù)

public interface CallBack {
    public void onSuccess(String response);
}
public interface Continuation<in T> {
    public fun resumeWith(result: Result<T>)
}

由此可以得出結(jié)論:Continuation本質(zhì)上就是CallBack只是多了一個帶有泛型的參數(shù)。

通過上面的分析可以得出結(jié)論 :掛起函數(shù)的本質(zhì)就是Callback

我們已知Continuation的本質(zhì)是CallBack,Continuation是延續(xù),就是接下來要做的事情,那么在省市區(qū)獲取的代碼其實就是這樣一個流程:

getProvincesCode(object : Continuation<String> {
    override fun resumeWith(result: Result<String>) {
        val provinceCode = result.getOrNull()
        println("provincesCode:$provinceCode")
        getCityCode(provinceCode, object : Continuation<String> {
            override fun resumeWith(result: Result<String>) {
                val cityCode = result.getOrNull()
                println("cityCode:$cityCode")
                getAreaCode(cityCode, object : Continuation<String> {
                    override fun resumeWith(result: Result<String>) {
                        val areaCode = result.getOrNull()
                        println("areaCode:$areaCode")
                    }
                })
            }
        })
    }
})

這個過程是編譯器在后面幫我們做的,實際編碼中這種編碼方式并不會出現(xiàn),畢竟掛起函數(shù)解決的就是這個問題。

3.協(xié)程與掛起函數(shù)

需要說明的是協(xié)程與掛起函數(shù)并不是同一個東西,我們再最開始使用suspend實現(xiàn)省市區(qū)三級聯(lián)動的時候用到了runBlocking,掛起函數(shù)的調(diào)用是需要一個協(xié)程作用域的,除了這點之外,在runBlocking的源碼中還有一點要注意:

public actual fun <T> runBlocking(
    context: CoroutineContext, 
    block: suspend CoroutineScope.() -> T): T {
    ...
}

第二個參數(shù)中有一個suspend,所以可以理解CoroutineScope.() -> T也是一個掛起函數(shù)那么被suspend關(guān)鍵字修飾的掛起函數(shù)可以運行在runBlocking中也就不難理解了。

那么我們就可以得到一個結(jié)論:掛起和恢復(fù)是協(xié)程的底層能力,而掛起函數(shù)是一種表現(xiàn)形式,通過suspend關(guān)鍵字修飾的函數(shù)可以讓我們在上層很方便的實現(xiàn)這種底層能力。

4.掛起函數(shù)是Kotlin協(xié)程的最大優(yōu)勢

開篇就提出了這個結(jié)論,最后再來總結(jié)一下這個能力:

  • 掛起函數(shù)在執(zhí)行中可以切換線程且不會對主線程造成阻塞;
  • 掛起函數(shù)有掛起和恢復(fù)的能力,可以極大地簡化異步編程,實現(xiàn)同步執(zhí)行異步任務(wù);
  • 掛起函數(shù)是Kotlin中特有的能力;

5.總結(jié)

  • 定義一個掛起函數(shù)只需要在普通函數(shù)上加上suspend關(guān)鍵字,而添加這個關(guān)鍵字之后函數(shù)類型就會被改變,如suspend (Int) -&gt; Double”與“(Int) -> Double并不是同一個類型;
  • 掛起函數(shù)具有掛起和恢復(fù)的能力,那么就會出現(xiàn)同一行代碼會在兩個線程中執(zhí)行,Kotlin編譯器會在后臺進行編譯;
  • 掛起函數(shù)的本質(zhì)就是CallBack。只是說,Kotlin 底層用了一個更加高大上的名字,叫 Continuation;
  • 掛起和恢復(fù)是一種底層能力,而上層的表現(xiàn)形式是掛起函數(shù);
  • 掛起函數(shù)只能在協(xié)程中被調(diào)用或者在掛起函數(shù)中調(diào)用,而協(xié)程中的block也是一個掛起函數(shù)。

以上就是Kotlin 協(xié)程與掛起函數(shù)及suspend關(guān)鍵字深入理解的詳細內(nèi)容,更多關(guān)于Kotlin 協(xié)程掛起函數(shù)suspend的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • android開發(fā)PathEffect問題處理

    android開發(fā)PathEffect問題處理

    本文主要整理了關(guān)于android中PathEffect的問題匯總以及處理方式,以及給大家做了關(guān)于PathEffect類的詳細解釋。
    2017-11-11
  • Android中微信小程序開發(fā)之彈出菜單

    Android中微信小程序開發(fā)之彈出菜單

    這篇文章主要介紹了Android中微信小程序開發(fā)之彈出菜單的相關(guān)資料,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2016-12-12
  • Android中貝塞爾曲線的繪制方法示例代碼

    Android中貝塞爾曲線的繪制方法示例代碼

    本文主要介紹Android中貝塞爾曲線的繪制方法,這里詳細介紹了如何繪制貝塞爾曲線,并附有示例代碼,有需要的小伙伴可以參考下
    2016-08-08
  • Android開發(fā)之利用ListView動態(tài)刷新某個Item

    Android開發(fā)之利用ListView動態(tài)刷新某個Item

    這篇文章主要介紹了Android開發(fā)之利用ListView動態(tài)刷新某個Item的方法,文章給出了詳解的示例代碼,相信對大家的理解和學(xué)習(xí)具有一定的參考借鑒價值,有需要的朋友們下面來一起跟著小編學(xué)習(xí)學(xué)習(xí)吧。
    2016-12-12
  • Android Compose remember的使用和原理詳解

    Android Compose remember的使用和原理詳解

    本文詳細介紹了Android Jetpack Compose中的remember函數(shù),它是優(yōu)化UI組件重組性能的重要工具,remember可以緩存數(shù)據(jù)或?qū)ο?避免每重組時重新計算,保持狀態(tài)不變,通過合理使用`remember`,可以提高應(yīng)用的性能和流暢度,感興趣的朋友一起看看吧
    2025-03-03
  • Dagger2新手入門與使用基礎(chǔ)教程

    Dagger2新手入門與使用基礎(chǔ)教程

    網(wǎng)上隨便搜索一下Dragger2,一大堆文章鋪天蓋地而來,但很多講的不清楚,下面這篇文章主要給大家介紹了關(guān)于Dagger2新手入門與使用基礎(chǔ)教程的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下。
    2018-04-04
  • Android 系統(tǒng)net和wap接入點的區(qū)別

    Android 系統(tǒng)net和wap接入點的區(qū)別

    這篇文章主要介紹了Android 系統(tǒng)net和wap接入點的區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2016-09-09
  • Android Studio導(dǎo)入項目非常慢的解決方法

    Android Studio導(dǎo)入項目非常慢的解決方法

    這篇文章主要為大家詳細介紹了Android Studio導(dǎo)入項目非常慢的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Android條目拖拽刪除功能實例代碼

    Android條目拖拽刪除功能實例代碼

    最近做項目遇到這樣的需求,要做條目條目拖拽刪除效果,實際效果和QQ消息刪除一樣,側(cè)滑有制定和刪除,下面通過本文給大家分享Android條目拖拽刪除功能,需要的朋友參考下吧
    2017-08-08
  • Android編程連接MongoDB及增刪改查等基本操作示例

    Android編程連接MongoDB及增刪改查等基本操作示例

    這篇文章主要介紹了Android編程連接MongoDB及增刪改查等基本操作,簡單介紹了MongoDB功能、概念、使用方法及Android操作MongoDB數(shù)據(jù)庫的基本技巧,需要的朋友可以參考下
    2017-07-07

最新評論

保靖县| 延津县| 江源县| 阿勒泰市| 农安县| 大荔县| 原平市| 京山县| 仁怀市| 綦江县| 三亚市| 黄浦区| 扎赉特旗| 江孜县| 永宁县| 成都市| 金华市| 敦煌市| 察哈| 武穴市| 安化县| 赣榆县| 日照市| 巨野县| 呼伦贝尔市| 绥江县| 阳泉市| 罗源县| 内丘县| 青浦区| 长岭县| 上饶市| 林州市| 大城县| 陵水| 鞍山市| 竹溪县| 汉中市| 盐城市| 忻城县| 枣庄市|