Kotlin 學習筆記(十四)淺讀協(xié)程

上一篇-Kotlin 學習筆記(十三)高階函數(shù)

為什么需要協(xié)程

??舉例一個異步編程中最常見的場景:后臺線程執(zhí)行一個A任務,下一個B任務依賴于A任務的執(zhí)行結果,所以必須等待上一個任務執(zhí)行完成后才能開始執(zhí)行。直接上代碼。

// 模擬本地創(chuàng)建 Token
fun requestToken(cb: (Token) -> Unit){
    //todo
}

//組裝Http請求
fun createHttpUtils(token: Token, item: Item, cb: (HttpUtils) -> Unit): {
    //todo 
}

//進行網(wǎng)絡請求
fun postUrl(httpUtils: HttpUtils) {
    //todo 
}

??如果在代碼中調用 會怎么樣呢?

fun postItem() {
    requestToken{ token ->
        createHttpUtils(token) { httpUtils->
            postUrl(httpUtils)
        }
    }
}

?? 可以看到 我們 嵌套了好幾層來實現(xiàn)一個網(wǎng)絡請求,可讀性變差,代碼也很容易出現(xiàn)問題。kotlin中的 協(xié)程可以幫助我們減少代碼嵌套,優(yōu)化線程/線程池控制。

協(xié)程使用

修改示例的方法,引入kotlin 中的協(xié)程我們再看一下 代碼會發(fā)生哪些變化

// 關于suspend  后邊總結,掛起函數(shù)的重點
suspend fun requestToken(): Token { ... }   // 掛起函數(shù)

 // 掛起函數(shù)
suspend fun createHttpUtils(token: Token): HttpUtils{ ... } 

fun postUrl(httpUtils: HttpUtils) { ... }

// 代碼使用
fun postItem() {
     //  注意:開發(fā)過程中不能直接使用GlobalScope.launch
    GlobalScope.launch {
        // 看似三個任務并行處理的,但其實是串聯(lián)執(zhí)行的
        val token = requestToken()
        val httpUtils= createHttpUtils(token)
        postUrl(httpUtils)
    }
}

??上述代碼,像代碼中的GlobalScope.launch不建議在日常開發(fā)中使用,此處只是為了簡單代碼展示。因為 GlobalScope 創(chuàng)建的協(xié)程沒有父協(xié)程,除非執(zhí)行完成或手動取消,該協(xié)程都會繼續(xù)運行,而實際上在 Android 中協(xié)程的運行需要跟 Activity/Fragment 的生命周期綁定。
??看完上邊的代碼使用,就已經(jīng)可以在項目中簡單使用了

了解協(xié)程

項目中引入

            // 協(xié)程    https://github.com/Kotlin/kotlinx.coroutines
            kotlin_coroutines      : [group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-android', version: versions.kotlin_coroutines],
            kotlin_coroutines_core : [group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: versions.kotlin_coroutines],

協(xié)程概念

協(xié)程通過將復雜性放入庫來簡化異步編程。程序的邏輯可以在協(xié)程中順序地表達,而底層庫會為我們解決其異步性。該庫可以將用戶代碼的相關部分包裝為回調、訂閱相關事件、在不同線程(甚至不同機器)上調度執(zhí)行,而代碼則保持如同順序執(zhí)行一樣簡單。 - 協(xié)程官方文檔翻譯

??協(xié)程的開發(fā)人員 Roman Elizarov 如下描述協(xié)程的:協(xié)程就像非常輕量級的線程。線程是由系統(tǒng)調度的,線程切換或線程阻塞的開銷都比較大。而協(xié)程依賴于線程,但是協(xié)程掛起時不需要阻塞線程,幾乎是無代價的,協(xié)程是由開發(fā)者控制的。所以協(xié)程也像用戶態(tài)的線程,非常輕量級,一個線程中可以創(chuàng)建任意個協(xié)程。

協(xié)程知識點

根據(jù)官方網(wǎng)站了解重要的知識點

掛起函數(shù)

//有suspend修飾符標記,這表示兩個函數(shù)都是掛起函數(shù)
suspend fun requestToken(): Token { ... } 
suspend fun createHttpUtils(token: Token): HttpUtils{ ... } 

??掛起函數(shù)能夠以與普通函數(shù)相同的方式獲取參數(shù)和返回值,但是調用函數(shù)可能掛起協(xié)程,掛起函數(shù)掛起協(xié)程時,不會阻塞協(xié)程所在的線程。掛起函數(shù)執(zhí)行完成后會恢復協(xié)程,后面的代碼才會繼續(xù)執(zhí)行。但是掛起函數(shù)只能在協(xié)程中或其他掛起函數(shù)中調用。

知識點

?? 要想明白協(xié)程的內在,還是需要看源碼

GlobalScope.launch

// launch 函數(shù)源碼
// CoroutineContext: 協(xié)程上下文 是一些元素的集合,包括 Job 和 CoroutineDispatcher 等元素。
// CoroutineDispatcher : 協(xié)程調度器,決定協(xié)程所在的線程或線程池  (類型 :Dispatchers.Default、Dispatchers.IO、Dispatchers.Main和Dispatchers.Unconfined)
// CoroutineScope.launch 默認使用Dispatchers.Default
public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    ...
}

對于源碼簡單的解釋 主要看注釋部分。

創(chuàng)建方式

  1. CoroutineScope.launch : CoroutineScope.launch {} 是最常用的 Coroutine builders,不阻塞當前線程,在后臺創(chuàng)建一個新協(xié)程,也可以指定協(xié)程調度器
  2. runBlocking : runBlocking {}是創(chuàng)建一個新的協(xié)程同時阻塞當前線程,直到協(xié)程結束。(個人很少用....)
  3. ** withContext**: withContext {}不會創(chuàng)建新的協(xié)程,在指定協(xié)程上運行掛起代碼塊,并掛起該協(xié)程直至代碼塊運行完成。
  4. **async ** : 類似CoroutineScope.launch,區(qū)別是 async 返回的Deferred 類型,Deferred 是 Job的子類,帶有返回值,但是 CoroutineScope.launch 返回的 Job類型,所以 CoroutineScope.launch沒有返回值。
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容