kotlin之協(xié)程(三),開始創(chuàng)建協(xié)程,launch,withContext

目錄

kotlin之協(xié)程(一),線程,進(jìn)程,協(xié)程,協(xié)程可以替換線程嗎?
kotlin之協(xié)程(二),Kotlin協(xié)程是什么、掛起是什么、掛起的非阻塞式
kotlin之協(xié)程(三),開始創(chuàng)建協(xié)程,launch,withContext
kotlin之協(xié)程(四),協(xié)程的核心關(guān)鍵字suspend
kotlin之協(xié)程(五),launch 函數(shù)以及協(xié)程的取消與超時
kotlin之協(xié)程(六),協(xié)程中的 async和launch的區(qū)別以及runBlocking
kotlin之協(xié)程(七),協(xié)程中relay、yield 區(qū)別

前言

在開始做安卓之前都是學(xué)習(xí)如何用框架,用一個庫,但是在用的過程中不了解核心本質(zhì),無法理解其含義.
后來就換了學(xué)習(xí)的方法
在用一個東西之前,都是先了解這個事物的本質(zhì),再去學(xué)習(xí)如何用,這樣的形式可能在用的過程中有更深的體會.在使用的過程中再去更深入的了解其如何實現(xiàn).

我們想在kotlin使用協(xié)程

項目中配置對 Kotlin 協(xié)程的支持

在使用協(xié)程之前,我們需要在 build.gradle 文件中增加對 Kotlin 協(xié)程的依賴:

  • 項目根目錄下的 build.gradle :
buildscript {

    ext.kotlin_coroutines = '1.4.0'

}
  • Module 下的 build.gradle
dependencies {

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'

}

創(chuàng)建協(xié)程

kotlin 中 GlobalScope 類提供了幾個攜程構(gòu)造函數(shù):

  • launch - 創(chuàng)建協(xié)程
  • async - 創(chuàng)建帶返回值的協(xié)程,返回的是 Deferred 類
  • withContext - 不創(chuàng)建新的協(xié)程,指定協(xié)程上運(yùn)行代碼塊
  • runBlocking - 不是 GlobalScope 的 API,可以獨(dú)立使用,區(qū)別是 runBlocking 里面的 delay 會阻塞線程,而 launch 創(chuàng)建的不會

先跑起來一個簡單的例子:

import kotlinx.coroutines.*

fun main() {
    GlobalScope.launch { // 在后臺啟動一個新的協(xié)程并繼續(xù)
        delay(1000L) // 非阻塞的等待 1 秒鐘(默認(rèn)時間單位是毫秒)
        println("World!") // 在延遲后打印輸出
    }
    println("Hello,") // 協(xié)程已在等待時主線程還在繼續(xù)
    Thread.sleep(2000L) // 阻塞主線程 2 秒鐘來保證 JVM 存活
}

協(xié)程最簡單的使用方法,其實在前面章節(jié)就已經(jīng)看到了。我們可以通過一個 launch 函數(shù)實現(xiàn)線程切換的功能

coroutineScope.launch(Dispatchers.IO) {
    ...
}

所以,什么時候用協(xié)程?當(dāng)你需要切線程或者指定線程的時候。你要在后臺執(zhí)行任務(wù)?切!

launch(Dispatchers.IO) {
    val image = getImage(imageId)
}

然后需要在前臺更新界面?再切!

coroutineScope.launch(Dispatchers.IO) {
    val image = getImage(imageId)
    launch(Dispatchers.Main) {
        avatarIv.setImageBitmap(image)
    }
}

好像有點不對勁?這不還是有嵌套嘛。

如果只是使用 launch 函數(shù),協(xié)程并不能比線程做更多的事。不過協(xié)程中卻有一個很實用的函數(shù):withContext 。這個函數(shù)可以切換到指定的線程,并在閉包內(nèi)的邏輯執(zhí)行結(jié)束之后,自動把線程切回去繼續(xù)執(zhí)行。那么可以將上面的代碼寫成這樣:

coroutineScope.launch(Dispatchers.Main) {      //  在 UI 線程開始
    val image = withContext(Dispatchers.IO) {  // 切換到 IO 線程,并在執(zhí)行完成后切回 UI 線程
        getImage(imageId)                      // 將會運(yùn)行在 IO 線程
    }
    avatarIv.setImageBitmap(image)             // 回到 UI 線程更新 UI
} 

我們甚至可以把 withContext 放進(jìn)一個單獨(dú)的函數(shù)里面:

launch(Dispatchers.Main) {              //  在 UI 線程開始
    val image = getImage(imageId)
    avatarIv.setImageBitmap(image)     //  執(zhí)行結(jié)束后,自動切換回 UI 線程
}
//                              
fun getImage(imageId: Int) = withContext(Dispatchers.IO) {
    ...
}

這就是之前說的「用同步的方式寫異步的代碼」了。

不過如果只是這樣寫,編譯器是會報錯的:

fun getImage(imageId: Int) = withContext(Dispatchers.IO) {
    // IDE 報錯 Suspend function'withContext' should be called only from a coroutine or another suspend funcion
}

意思是說,withContext 是一個 suspend 函數(shù),它需要在協(xié)程或者是另一個 suspend 函數(shù)中調(diào)用。
what?

suspend函數(shù)我們在kotlin之協(xié)程(二),Kotlin協(xié)程是什么、掛起是什么、掛起的非阻塞式有提到,下一章我們專門講解kotlin協(xié)程中的suspend

(每天學(xué)習(xí)一點點.每天進(jìn)步一點點,分享不宜路過點個贊呀,喜歡的點個關(guān)注后續(xù)更新不斷)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容