那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Kotlin協(xié)程實(shí)戰(zhàn)進(jìn)階之筑基篇2

jf_78858299 ? 來(lái)源:小余的自習(xí)室 ? 作者:蘇火火 ? 2023-05-30 16:25 ? 次閱讀
launch 創(chuàng)建子協(xié)程

通過(guò)launch在一個(gè)協(xié)程中啟動(dòng)子協(xié)程,可以根據(jù)業(yè)務(wù)需求創(chuàng)建一個(gè)或多個(gè)子協(xié)程:

fun launchTest3() {
    print("start")
    GlobalScope.launch {
        delay(1000)
        print("CoroutineScope.launch")

        //在協(xié)程內(nèi)創(chuàng)建子協(xié)程
        launch {
            delay(1500)//1.5秒無(wú)阻塞延遲(默認(rèn)單位為毫秒)
            print("launch 子協(xié)程")
        }
    }
    print("end")
}

打印數(shù)據(jù)如下:

圖片

launch3.gif

async

async類(lèi)似于launch,都是創(chuàng)建一個(gè)不會(huì)阻塞當(dāng)前線(xiàn)程的新的協(xié)程。它們區(qū)別在于:async的返回是Deferred對(duì)象,可通過(guò)Deffer.await()等待協(xié)程執(zhí)行完成并獲取結(jié)果,而 launch 不行。常用于并發(fā)執(zhí)行-同步等待和獲取返回值的情況。

public fun  CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
): Deferred
  • context:?協(xié)程的上下文,同launch
  • start: ?協(xié)程啟動(dòng)模式,同launch
  • block:??協(xié)程代碼,同launch
  • Deferred:?協(xié)程構(gòu)建函數(shù)的返回值,繼承自Job,一個(gè)有結(jié)果的Job,可通過(guò)Deffer.await()等待協(xié)程執(zhí)行完成并獲取結(jié)果。
await 獲取返回值
//獲取返回值
fun asyncTest1() {
    print("start")
    GlobalScope.launch {
        val deferred: Deferred<String> = async {
            //協(xié)程將線(xiàn)程的執(zhí)行權(quán)交出去,該線(xiàn)程繼續(xù)干它要干的事情,到時(shí)間后會(huì)恢復(fù)至此繼續(xù)向下執(zhí)行
            delay(2000)//2秒無(wú)阻塞延遲(默認(rèn)單位為毫秒)
            print("asyncOne")
            "HelloWord"//這里返回值為HelloWord
        }

        //等待async執(zhí)行完成獲取返回值,此處并不會(huì)阻塞線(xiàn)程,而是掛起,將線(xiàn)程的執(zhí)行權(quán)交出去
        //等到async的協(xié)程體執(zhí)行完畢后,會(huì)恢復(fù)協(xié)程繼續(xù)往下執(zhí)行
        val result = deferred.await()
        print("result == $result")
    }
    print("end")
}

上面例子中返回對(duì)象Deferred, 通過(guò)函數(shù)await()獲取結(jié)果值。打印數(shù)據(jù)如下:

圖片

async1.gif

注意:await() 不能在協(xié)程之外調(diào)用,因?yàn)樗枰獟炱鹬钡接?jì)算完成,而且只有協(xié)程可以以非阻塞的方式掛起。所以把它放到協(xié)程中。

async 并發(fā)

當(dāng)在協(xié)程作用域中使用async函數(shù)時(shí)可以創(chuàng)建并發(fā)任務(wù):

fun asyncTest2() {
    print("start")
    GlobalScope.launch {
        val time = measureTimeMillis {//計(jì)算執(zhí)行時(shí)間
            val deferredOne: Deferred<Int> = async {
                delay(2000)
                print("asyncOne")
                100//這里返回值為100
            }

            val deferredTwo: Deferred<Int> = async {
                delay(3000)
                print("asyncTwo")
                200//這里返回值為200
            }

            val deferredThr: Deferred<Int> = async {
                delay(4000)
                print("asyncThr")
                300//這里返回值為300
            }

            //等待所有需要結(jié)果的協(xié)程完成獲取執(zhí)行結(jié)果
            val result = deferredOne.await() + deferredTwo.await() + deferredThr.await()
            print("result == $result")
        }
        print("耗時(shí) $time ms")
    }
    print("end")
}

打印數(shù)據(jù)如下:

圖片

async2.gif

上面的代碼就是一個(gè)簡(jiǎn)單的并發(fā)示例,async是不阻塞線(xiàn)程的,也就是說(shuō)上面三個(gè)async{}異步任務(wù)是同時(shí)進(jìn)行的。通過(guò)await()方法可以拿到async協(xié)程的執(zhí)行結(jié)果,可以看到兩個(gè)協(xié)程的總耗時(shí)是遠(yuǎn)少于9秒的,總耗時(shí)基本等于耗時(shí)最長(zhǎng)的協(xié)程。

1.Deferred集合還可以使用awaitAll()等待全部完成;

2.如果Deferred不執(zhí)行await()async內(nèi)部拋出的異常不會(huì)被logCattryCatch捕獲, 但是依然會(huì)導(dǎo)致作用域取消和異常崩潰; 但當(dāng)執(zhí)行await時(shí)異常信息會(huì)重新拋出。

3.惰性并發(fā),如果將async函數(shù)中的啟動(dòng)模式設(shè)置為CoroutineStart.LAZY懶加載模式時(shí)則只有調(diào)用Deferred對(duì)象的await時(shí)(或者執(zhí)行async.satrt())才會(huì)開(kāi)始執(zhí)行異步任務(wù)。

launch構(gòu)建器適合執(zhí)行 "一勞永逸" 的工作,意思就是說(shuō)它可以啟動(dòng)新協(xié)程而不需要結(jié)果返回;async構(gòu)建器可啟動(dòng)新協(xié)程并允許您使用一個(gè)名為await的掛起函數(shù)返回result,并且支持并發(fā)。另外launchasync之間的很大差異是它們對(duì)異常的處理方式不同。如果使用async作為最外層協(xié)程的開(kāi)啟方式,它期望最終是通過(guò)調(diào)用 await 來(lái)獲取結(jié)果 (或者異常),所以默認(rèn)情況下它不會(huì)拋出異常。這意味著如果使用 async啟動(dòng)新的最外層協(xié)程,而不使用await,它會(huì)靜默地將異常丟棄。

2.Job & Deferred

反觀線(xiàn)程,java平臺(tái)上很明確地給出了線(xiàn)程的類(lèi)型Thread,我們也需要一個(gè)這樣的類(lèi)來(lái)描述協(xié)程,它就是Job。它的API設(shè)計(jì)與Java的Thread殊途同歸。

Job

Job 是協(xié)程的句柄。如果把門(mén)和門(mén)把手比作協(xié)程和Job之間的關(guān)系,那么協(xié)程就是這扇門(mén),Job就是門(mén)把手。意思就是可以通過(guò)Job實(shí)現(xiàn)對(duì)協(xié)程的控制和管理。

從上面可以知道Joblaunch構(gòu)建協(xié)程返回的一個(gè)協(xié)程任務(wù),完成時(shí)是沒(méi)有返回值的。可以把Job看成協(xié)程對(duì)象本身,封裝了協(xié)程中需要執(zhí)行的代碼邏輯,協(xié)程的操作方法都在Job身上。Job具有生命周期并且可以取消,它也是上下文元素,繼承自CoroutineContext

這里列舉Job幾個(gè)比較有用的函數(shù):

public interface Job : CoroutineContext.Element {
    //活躍的,是否仍在執(zhí)行
    public val isActive: Boolean

    //啟動(dòng)協(xié)程,如果啟動(dòng)了協(xié)程,則為true;如果協(xié)程已經(jīng)啟動(dòng)或完成,則為false
    public fun start(): Boolean

    //取消Job,可通過(guò)傳入Exception說(shuō)明具體原因
    public fun cancel(cause: CancellationException? = null)

    //掛起協(xié)程直到此Job完成
    public suspend fun join()

    //取消任務(wù)并等待任務(wù)完成,結(jié)合了[cancel]和[join]的調(diào)用
    public suspend fun Job.cancelAndJoin() 

    //給Job設(shè)置一個(gè)完成通知,當(dāng)Job執(zhí)行完成的時(shí)候會(huì)同步執(zhí)行這個(gè)函數(shù)
    public fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle
}

Thread相比,Job同樣有join(),調(diào)用時(shí)會(huì)掛起(線(xiàn)程的join()則會(huì)阻塞線(xiàn)程),直到協(xié)程完成;它的cancel()可以類(lèi)比Threadinterrupt(),用于取消協(xié)程;isActive則是可以類(lèi)比ThreadisAlive(),用于查詢(xún)協(xié)程是否仍在執(zhí)行。

Job是一個(gè)接口類(lèi)型,它具有以下三種狀態(tài):

狀態(tài) 說(shuō)明
isActive 活躍的。當(dāng)Job處于活動(dòng)狀態(tài)時(shí)為true,如果Job已經(jīng)開(kāi)始,但還沒(méi)有完成、也沒(méi)有取消或者失敗,則是處于active狀態(tài)。
isCompleted 已完成。當(dāng)Job由于任何原因完成時(shí)為true,已取消、已失敗和已完成Job都是被視為完成狀態(tài)。
isCancelled 已退出。當(dāng)Job由于任何原因被取消時(shí)為true,無(wú)論是通過(guò)顯式調(diào)用cancel或這因?yàn)樗呀?jīng)失敗亦或者它的子或父被取消,都是被視為已退出狀態(tài)。

這里模擬一個(gè)無(wú)限循環(huán)的協(xié)程,當(dāng)協(xié)程是活躍狀態(tài)時(shí)每秒鐘打印兩次消息,1.2秒后取消協(xié)程:

fun jobTest() = runBlocking {
    val startTime = System.currentTimeMillis()
    val job = launch(Dispatchers.Default){
        var nextPrintTime = startTime
        var i = 0

        while (isActive) {//當(dāng)job是活躍狀態(tài)繼續(xù)執(zhí)行
            if (System.currentTimeMillis() >= nextPrintTime) {//每秒鐘打印兩次消息
                print("job: I'm sleeping ${i++} ...")
                nextPrintTime += 500
            }
        }
    }

    delay(1200)//延遲1.2s
    print("等待1.2秒后")


    //job.join()
    //job.cancel()
    job.cancelAndJoin()//取消任務(wù)并等待任務(wù)完成
    print("協(xié)程被取消并等待完成")
}

join()是一個(gè)掛起函數(shù),它需要等待協(xié)程的執(zhí)行,如果協(xié)程尚未完成,join()立即掛起,直到協(xié)程完成;如果協(xié)程已經(jīng)完成,join()不會(huì)掛起,而是立即返回。打印數(shù)據(jù)如下:

圖片

join.gif

Job 還可以有層級(jí)關(guān)系,一個(gè)Job可以包含多個(gè)子Job,當(dāng)父Job被取消后,所有的子Job也會(huì)被自動(dòng)取消;當(dāng)子Job被取消或者出現(xiàn)異常后父Job也會(huì)被取消。具有多個(gè)子 Job 的父Job 會(huì)等待所有子Job完成(或者取消)后,自己才會(huì)執(zhí)行完成。

總的來(lái)說(shuō):它的作用是Job實(shí)例作為協(xié)程的唯一標(biāo)識(shí),用于處理協(xié)程,并且負(fù)責(zé)管理協(xié)程的生命周期。

Deferred

Deferred繼承自Job,具有與Job相同的狀態(tài)機(jī)制。它是async構(gòu)建協(xié)程返回的一個(gè)協(xié)程任務(wù),可通過(guò)調(diào)用await()方法等待協(xié)程執(zhí)行完成并獲取結(jié)果。不同的是Job沒(méi)有結(jié)果值,Deffer有結(jié)果值。

public interface Deferred<out T> : Job {
    //等待協(xié)程執(zhí)行完成并獲取結(jié)果
    public suspend fun await(): T
}
  • await():?等待協(xié)程執(zhí)行完畢并返回結(jié)果,如果異常結(jié)束則會(huì)拋出異常;如果協(xié)程尚未完成,則掛起直到協(xié)程執(zhí)行完成。
  • T:????這里多了一個(gè)泛型參數(shù)T,它表示返回值類(lèi)型,通過(guò)await()函數(shù)可以拿到這個(gè)返回值。

上面已有Deferred代碼演示,這里就不再重復(fù)實(shí)踐。

3.作用域

通常我們提到的,都是用來(lái)描述范圍的,既有約束作用又有提供額外能力的作用。

協(xié)程作用域(CoroutineScope)其實(shí)就是為協(xié)程定義的作用范圍 ,為了確保所有的協(xié)程都會(huì)被追蹤,Kotlin 不允許在沒(méi)有使用CoroutineScope的情況下啟動(dòng)新的協(xié)程。CoroutineScope可被看作是一個(gè)具有超能力的ExecutorService的輕量級(jí)版本。它能啟動(dòng)新的協(xié)程,同時(shí)這個(gè)協(xié)程還具備上面所說(shuō)的suspendresume的優(yōu)勢(shì)。

每個(gè)協(xié)程生成器launchasync等都是CoroutineScope的擴(kuò)展,并繼承了它的coroutineContext自動(dòng)傳播其所有元素和取消。協(xié)程作用域本質(zhì)是一個(gè)接口:

public interface CoroutineScope {
    //此域的上下文。Context被作用域封裝,用于在作用域上擴(kuò)展的協(xié)程構(gòu)建器的實(shí)現(xiàn)。
    public val coroutineContext: CoroutineContext
}

因?yàn)?啟動(dòng)協(xié)程需要作用域 ,但是作用域又是在協(xié)程創(chuàng)建過(guò)程中產(chǎn)生的,這似乎是一個(gè)“先有雞后有蛋還是先有蛋后有雞”的問(wèn)題。

常用作用域

官方庫(kù)給我們提供了一些作用域可以直接來(lái)使用:

  • runBlocking:頂層函數(shù),它的第二個(gè)參數(shù)為接收者是CoroutineScope的函數(shù)字面量,可啟動(dòng)協(xié)程。但是它會(huì)阻塞當(dāng)前線(xiàn)程,主要用于測(cè)試。
  • GlobalScope:全局協(xié)程作用域,通過(guò)GlobalScope創(chuàng)建的協(xié)程不會(huì)有父協(xié)程,可以把它稱(chēng)為根協(xié)程。它啟動(dòng)的協(xié)程的生命周期只受整個(gè)應(yīng)用程序的生命周期的限制,且不能取消,在運(yùn)行時(shí)會(huì)消耗一些內(nèi)存資源,這可能會(huì)導(dǎo)致內(nèi)存泄露,所以仍不適用于業(yè)務(wù)開(kāi)發(fā)。
  • coroutineScope:創(chuàng)建一個(gè)獨(dú)立的協(xié)程作用域,直到所有啟動(dòng)的協(xié)程都完成后才結(jié)束自身。它是一個(gè)掛起函數(shù),需要運(yùn)行在協(xié)程內(nèi)或掛起函數(shù)內(nèi)。當(dāng)這個(gè)作用域中的任何一個(gè)子協(xié)程失敗時(shí),這個(gè)作用域失敗,所有其他的子程序都被取消。為并行分解工作而設(shè)計(jì)的。
  • supervisorScope:與coroutineScope類(lèi)似,不同的是子協(xié)程的異常不會(huì)影響父協(xié)程,也不會(huì)影響其他子協(xié)程。(作用域本身的失敗(在block或取消中拋出異常)會(huì)導(dǎo)致作用域及其所有子協(xié)程失敗,但不會(huì)取消父協(xié)程。)
  • MainScope:為UI組件創(chuàng)建主作用域。一個(gè)頂層函數(shù),上下文是SupervisorJob() + Dispatchers.Main,說(shuō)明它是一個(gè)在主線(xiàn)程執(zhí)行的協(xié)程作用域,通過(guò)cancel對(duì)協(xié)程進(jìn)行取消。推薦使用。
fun scopeTest() {
    //創(chuàng)建一個(gè)根協(xié)程
    GlobalScope.launch {//父協(xié)程
        launch {//子協(xié)程
            print("GlobalScope的子協(xié)程")
        }
        launch {//第二個(gè)子協(xié)程
            print("GlobalScope的第二個(gè)子協(xié)程")
        }
    }

    //為UI組件創(chuàng)建主作用域
    val mainScope = MainScope()
    mainScope.launch {//啟動(dòng)協(xié)程
        //todo
    }
}

注意:MainScope作用域的好處就是方便地綁定到UI組件的聲明周期上,在Activity銷(xiāo)毀的時(shí)候mainScope.cancel()取消其作用域。

Lifecycle的協(xié)程支持

Android 官方對(duì)協(xié)程的支持是非常友好的,KTX 為 Jetpack 的Lifecycle相關(guān)組件提供了已經(jīng)綁定UV聲明周期的作用域供我們直接使用:

  • lifecycleScopeLifecycle Ktx庫(kù)提供的具有生命周期感知的協(xié)程作用域,與Lifecycle綁定生命周期,生命周期被銷(xiāo)毀時(shí),此作用域?qū)⒈蝗∠?huì)與當(dāng)前的UI組件綁定生命周期,界面銷(xiāo)毀時(shí)該協(xié)程作用域?qū)⒈蝗∠粫?huì)造成協(xié)程泄漏,推薦使用。
  • viewModelScope:與lifecycleScope類(lèi)似,與ViewModel綁定生命周期,當(dāng)ViewModel被清除時(shí),這個(gè)作用域?qū)⒈蝗∠M扑]使用。

build.gradle添加Lifecycle相應(yīng)基礎(chǔ)組件后,再添加以下組件即可:

// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
// 只有Lifecycles(沒(méi)有 ViewModel 和 LiveData)
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"

因?yàn)?code>Activity 實(shí)現(xiàn)了LifecycleOwner這個(gè)接口,而lifecycleScope則正是它的拓展成員,可以在Activity中直接使用lifecycleScope協(xié)程實(shí)例:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btn_data.setOnClickListener {
            lifecycleScope.launch {//使用lifecycleScope創(chuàng)建協(xié)程
                //協(xié)程執(zhí)行體
            }
        }
    }
}

ViewModel中使用創(chuàng)建協(xié)程:

class MainViewModel : ViewModel() {
    fun getData() {
        viewModelScope.launch {//使用viewModelScope創(chuàng)建協(xié)程
            //執(zhí)行協(xié)程
        }
    }
}

注意:VIewModel 的作用域會(huì)在它的 clear 函數(shù)調(diào)用時(shí)取消。

分類(lèi)和行為規(guī)則

官方框架在實(shí)現(xiàn)復(fù)合協(xié)程的過(guò)程中也提供了作用域,主要用于明確父子關(guān)系,以及取消或者異常處理等方面的傳播行為。該作用域分為以下三種:

  • 頂級(jí)作用域 :沒(méi)有父協(xié)程的協(xié)程所在的作用域?yàn)轫敿?jí)作用域。
  • 協(xié)同作用域 :協(xié)程中啟動(dòng)新的協(xié)程,新協(xié)程為所在協(xié)程的子協(xié)程,這種情況下,子協(xié)程所在的作用域默認(rèn)為協(xié)同作用域。此時(shí)子協(xié)程拋出的未捕獲異常,都將傳遞給父協(xié)程處理,父協(xié)程同時(shí)也會(huì)被取消。
  • 主從作用域 :與協(xié)同作用域在協(xié)程的父子關(guān)系上一致,區(qū)別在于,處于該作用域下的協(xié)程出現(xiàn)未捕獲的異常時(shí),不會(huì)將異常向上傳遞給父協(xié)程。除了三種作用域中提到的行為以外,父子協(xié)程之間還存在以下規(guī)則:
  • 父協(xié)程被取消,則所有子協(xié)程均被取消。由于協(xié)同作用域和主從作用域中都存在父子協(xié)程關(guān)系,因此此條規(guī)則都適用。
  • 父協(xié)程需要等待子協(xié)程執(zhí)行完畢之后才會(huì)最終進(jìn)入完成狀態(tài),不管父協(xié)程自身的協(xié)程體是否已經(jīng)執(zhí)行完。
  • 子協(xié)程會(huì)繼承父協(xié)程的協(xié)程上下文中的元素,如果自身有相同key的成員,則覆蓋對(duì)應(yīng)的key,覆蓋的效果僅限自身范圍內(nèi)有效。

4.調(diào)度器

在上面介紹協(xié)程概念的時(shí)候,協(xié)程的掛起與恢復(fù)在哪掛起,什么時(shí)候恢復(fù),為什么能切換線(xiàn)程,這因?yàn)檎{(diào)度器的作用:它確定相應(yīng)的協(xié)程使用那些線(xiàn)程來(lái)執(zhí)行。

CoroutineDispatcher調(diào)度器指定指定執(zhí)行協(xié)程的目標(biāo)載體,它確定了相關(guān)的協(xié)程在哪個(gè)線(xiàn)程或哪些線(xiàn)程上執(zhí)行。可以將協(xié)程限制在一個(gè)特定的線(xiàn)程執(zhí)行,或?qū)⑺峙傻揭粋€(gè)線(xiàn)程池,亦或是讓它不受限地運(yùn)行。

協(xié)程需要調(diào)度的位置就是掛起點(diǎn)的位置,只有當(dāng)掛起點(diǎn)正在掛起的時(shí)候才會(huì)進(jìn)行調(diào)度,實(shí)現(xiàn)調(diào)度需要使用協(xié)程的攔截器。調(diào)度的本質(zhì)就是解決掛起點(diǎn)恢復(fù)之后的協(xié)程邏輯在哪里運(yùn)行的問(wèn)題。調(diào)度器也屬于協(xié)程上下文一類(lèi),它繼承自攔截器:

public abstract class CoroutineDispatcher :
    AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {

    //詢(xún)問(wèn)調(diào)度器是否需要分發(fā)
    public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true

    //將可運(yùn)行塊的執(zhí)行分派到給定上下文中的另一個(gè)線(xiàn)程上。這個(gè)方法應(yīng)該保證給定的[block]最終會(huì)被調(diào)用。
    public abstract fun dispatch(context: CoroutineContext, block: Runnable)

    //返回一個(gè)continuation,它封裝了提供的[continuation],攔截了所有的恢復(fù)。
    public final override fun  interceptContinuation(continuation: Continuation<T>): Continuation

它是所有協(xié)程調(diào)度程序?qū)崿F(xiàn)擴(kuò)展的基類(lèi)(我們很少會(huì)自己自定義調(diào)度器)。可以使用newSingleThreadContextnewFixedThreadPoolContext創(chuàng)建私有線(xiàn)程池。也可以使用asCoroutineDispatcher擴(kuò)展函數(shù)將任意java.util.concurrent.Executor轉(zhuǎn)換為調(diào)度程序。

調(diào)度器模式

Kotlin 提供了四個(gè)調(diào)度器,您可以使用它們來(lái)指定應(yīng)在何處運(yùn)行協(xié)程:

調(diào)度器模式 說(shuō)明 適用場(chǎng)景
Dispatchers.Default 默認(rèn)調(diào)度器,非主線(xiàn)程。CPU密集型任務(wù)調(diào)度器,適合處理后臺(tái)計(jì)算。 通常處理一些單純的計(jì)算任務(wù),或者執(zhí)行時(shí)間較短任務(wù)比如:Json的解析,數(shù)據(jù)計(jì)算等。
Dispatchers.Main UI調(diào)度器, Andorid 上的主線(xiàn)程。 調(diào)度程序是單線(xiàn)程的,通常用于UI交互,刷新等。
Dispatchers.Unconfined 一個(gè)不局限于任何特定線(xiàn)程的協(xié)程調(diào)度程序,即非受限調(diào)度器。 子協(xié)程切換線(xiàn)程代碼會(huì)運(yùn)行在原來(lái)的線(xiàn)程上,協(xié)程在相應(yīng)的掛起函數(shù)使用的任何線(xiàn)程中繼續(xù)。
Dispatchers.IO IO調(diào)度器,非主線(xiàn)程,執(zhí)行的線(xiàn)程是IO線(xiàn)程。 適合執(zhí)行IO相關(guān)操作,比如:網(wǎng)絡(luò)處理,數(shù)據(jù)庫(kù)操作,文件讀寫(xiě)等。

所有的協(xié)程構(gòu)造器(如launchasync)都接受一個(gè)可選參數(shù),即 CoroutineContext ,該參數(shù)可用于顯式指定要?jiǎng)?chuàng)建的協(xié)程和其它上下文元素所要使用的CoroutineDispatcher

fun dispatchersTest() {
    //創(chuàng)建一個(gè)在主線(xiàn)程執(zhí)行的協(xié)程作用域
    val mainScope = MainScope()
    mainScope.launch {
        launch(Dispatchers.Main) {//在協(xié)程上下參數(shù)中指定調(diào)度器
            print("主線(xiàn)程調(diào)度器")
        }
        launch(Dispatchers.Default) {
            print("默認(rèn)調(diào)度器")
        }
        launch(Dispatchers.Unconfined) {
            print("任意調(diào)度器")
        }
        launch(Dispatchers.IO) {
            print("IO調(diào)度器")
        }
    }
}

打印數(shù)據(jù)如下:

圖片

image.png

withContext

在 Andorid 開(kāi)發(fā)中,我們常常在子線(xiàn)程中請(qǐng)求網(wǎng)絡(luò)獲取數(shù)據(jù),然后切換到主線(xiàn)程更新UI。官方為我們提供了一個(gè)withContext頂級(jí)函數(shù),在獲取數(shù)據(jù)函數(shù)內(nèi),調(diào)用withContext(Dispatchers.IO)來(lái)創(chuàng)建一個(gè)在IO線(xiàn)程池中運(yùn)行的塊。您放在該塊內(nèi)的任何代碼都始終通過(guò)IO調(diào)度器執(zhí)行。由于withContext本身就是一個(gè)suspend函數(shù),它會(huì)使用協(xié)程來(lái)保證主線(xiàn)程安全。

//用給定的協(xié)程上下文調(diào)用指定的掛起塊,掛起直到它完成,并返回結(jié)果。
public suspend fun  withContext(
    context: CoroutineContext,
    block: suspend CoroutineScope.() -> T
): T
  • context:?協(xié)程的上下文,同上(調(diào)度器也屬于上下文一類(lèi))。
  • block:??協(xié)程執(zhí)行體,同上。block中的代碼會(huì)被調(diào)度到上面指定的調(diào)度器上執(zhí)行,并返回結(jié)果值。

這個(gè)函數(shù)會(huì)使用新指定的上下文的dispatcher,將block的執(zhí)行轉(zhuǎn)移到指定的線(xiàn)程中。它會(huì)返回結(jié)果, 可以和當(dāng)前協(xié)程的父協(xié)程存在交互關(guān)系, 主要作用為了來(lái)回切換調(diào)度器

GlobalScope.launch(Dispatchers.Main) {//開(kāi)始協(xié)程:主線(xiàn)程
    val result: User = withContext(Dispatchers.IO) {//網(wǎng)絡(luò)請(qǐng)求(IO 線(xiàn)程)
        userApi.getUserSuspend("FollowExcellence")
    }
    tv_title.text = result.name //更新 UI(主線(xiàn)程)
}

在主線(xiàn)程中啟動(dòng)一個(gè)協(xié)程,然后再通過(guò)withContext(Dispatchers.IO)調(diào)度到IO線(xiàn)程上去做網(wǎng)絡(luò)請(qǐng)求,獲取結(jié)果返回后,主線(xiàn)程上的協(xié)程就會(huì)恢復(fù)繼續(xù)執(zhí)行,完成UI的更新。

由于withContext可讓在不引入回調(diào)的情況下控制任何代碼行的線(xiàn)程池,因此可以將其應(yīng)用于非常小的函數(shù),如從數(shù)據(jù)庫(kù)中讀取數(shù)據(jù)或執(zhí)行網(wǎng)絡(luò)請(qǐng)求。一種不錯(cuò)的做法是使用withContext來(lái)確保每個(gè)函數(shù)都是主線(xiàn)程安全的,那么可以從主線(xiàn)程調(diào)用每個(gè)函數(shù)。調(diào)用方也就無(wú)需再考慮應(yīng)該使用哪個(gè)線(xiàn)程來(lái)執(zhí)行函數(shù)了。您可以使用外部 withContext來(lái)讓 Kotlin 只切換一次線(xiàn)程,這樣可以在多次調(diào)用的情況下,以盡可能避免了線(xiàn)程切換所帶來(lái)的性能損失。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Android
    +關(guān)注

    關(guān)注

    12

    文章

    3945

    瀏覽量

    127947
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2975

    瀏覽量

    105156
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3637

    瀏覽量

    93989
  • ui
    ui
    +關(guān)注

    關(guān)注

    0

    文章

    204

    瀏覽量

    21419
  • kotlin
    +關(guān)注

    關(guān)注

    0

    文章

    60

    瀏覽量

    4211
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    談?wù)?b class='flag-5'>協(xié)的那些事兒

    隨著異步編程的發(fā)展以及各種并發(fā)框架的普及,協(xié)作為一種異步編程規(guī)范在各類(lèi)語(yǔ)言中地位逐步提高。我們不單單會(huì)在自己的程序中使用協(xié),各類(lèi)框架如fastapi,aiohttp等也都是基于異步
    的頭像 發(fā)表于 01-26 11:36 ?1167次閱讀
    談?wù)?b class='flag-5'>協(xié)</b><b class='flag-5'>程</b>的那些事兒

    Python中的多核CPU共享數(shù)據(jù)協(xié)詳解

    協(xié)又稱(chēng)微線(xiàn)程,coroutne,協(xié)是一種用戶(hù)態(tài)的輕量級(jí)線(xiàn)程。通俗點(diǎn)講就是周末我在家里休息,假如我先洗漱,再煮飯,再下載電影看會(huì)很慢,用了協(xié)
    的頭像 發(fā)表于 12-07 10:23 ?6693次閱讀
    Python中的多核CPU共享數(shù)據(jù)<b class='flag-5'>之</b><b class='flag-5'>協(xié)</b><b class='flag-5'>程</b>詳解

    Python自動(dòng)化運(yùn)維協(xié)函數(shù)賦值過(guò)程

    協(xié)的優(yōu)點(diǎn):(1)無(wú)需線(xiàn)程上下文切換的開(kāi)銷(xiāo),協(xié)避免了無(wú)意義的調(diào)度,由此可以提高性能(但也因此,程序員必須自己承擔(dān)調(diào)度的責(zé)任,同時(shí),協(xié)
    的頭像 發(fā)表于 03-18 11:22 ?3768次閱讀

    關(guān)于C++ 20協(xié)最全面詳解

    花了一兩周的時(shí)間后,我想寫(xiě)寫(xiě) C++20 協(xié)的基本用法,因?yàn)?C++ 的協(xié)讓我感到很奇怪,寫(xiě)一個(gè)協(xié)
    的頭像 發(fā)表于 04-12 11:10 ?1.3w次閱讀
    關(guān)于C++ 20<b class='flag-5'>協(xié)</b><b class='flag-5'>程</b>最全面詳解

    Python后端項(xiàng)目的協(xié)是什么

    最近公司 Python 后端項(xiàng)目進(jìn)行重構(gòu),整個(gè)后端邏輯基本都變更為采用“異步”協(xié)的方式實(shí)現(xiàn)。看著滿(mǎn)屏幕經(jīng)過(guò) async await(協(xié)在 Python 中的實(shí)現(xiàn))修飾的代碼,我頓時(shí)
    的頭像 發(fā)表于 09-23 14:38 ?1370次閱讀

    Python協(xié)與JavaScript協(xié)的對(duì)比及經(jīng)驗(yàn)技巧

    前言以前沒(méi)怎么接觸前端,對(duì) JavaScript 的異步操作不了解,現(xiàn)在有了點(diǎn)了解。一查發(fā)現(xiàn) Python 和 JavaScript 的協(xié)發(fā)展史簡(jiǎn)直就是一毛一樣!這里大致做下橫向?qū)Ρ群涂偨Y(jié),便于
    的頭像 發(fā)表于 10-20 14:30 ?1979次閱讀

    使用channel控制協(xié)數(shù)量

    goroutine 是輕量級(jí)線(xiàn)程,調(diào)度由 Go 運(yùn)行時(shí)進(jìn)行管理的。Go 語(yǔ)言的并發(fā)控制主要使用關(guān)鍵字 go 開(kāi)啟協(xié) goroutine。Go 協(xié)(Goroutine)之間通過(guò)信道(
    的頭像 發(fā)表于 09-19 15:06 ?1180次閱讀

    詳解Linux線(xiàn)程、線(xiàn)程與異步編程、協(xié)與異步

    協(xié)不是系統(tǒng)級(jí)線(xiàn)程,很多時(shí)候協(xié)被稱(chēng)為“輕量級(jí)線(xiàn)程”、“微線(xiàn)程”、“纖(fiber)”等。簡(jiǎn)單來(lái)說(shuō)可以認(rèn)為
    的頭像 發(fā)表于 03-16 15:49 ?1039次閱讀

    協(xié)的概念及協(xié)的掛起函數(shù)介紹

    協(xié)是一種輕量級(jí)的線(xiàn)程,它可以在單個(gè)線(xiàn)程中實(shí)現(xiàn)并發(fā)執(zhí)行。與線(xiàn)程不同,協(xié)不需要操作系統(tǒng)的上下文切換,因此可以更高效地使用系統(tǒng)資源。Kotlin
    的頭像 發(fā)表于 04-19 10:20 ?942次閱讀

    Kotlin協(xié)實(shí)戰(zhàn)進(jìn)階1

    協(xié)的概念在1958年就開(kāi)始出現(xiàn)(比線(xiàn)程還早), 目前很多語(yǔ)言開(kāi)始原生支, Java 沒(méi)有原生協(xié)但是大型公司都自己或者使用第三方庫(kù)來(lái)支持協(xié)
    的頭像 發(fā)表于 05-30 16:24 ?761次閱讀
    <b class='flag-5'>Kotlin</b><b class='flag-5'>協(xié)</b><b class='flag-5'>程</b><b class='flag-5'>實(shí)戰(zhàn)</b><b class='flag-5'>進(jìn)階</b><b class='flag-5'>之</b><b class='flag-5'>筑</b><b class='flag-5'>基</b><b class='flag-5'>篇</b>1

    Kotlin協(xié)實(shí)戰(zhàn)進(jìn)階3

    協(xié)的概念在1958年就開(kāi)始出現(xiàn)(比線(xiàn)程還早), 目前很多語(yǔ)言開(kāi)始原生支, Java 沒(méi)有原生協(xié)但是大型公司都自己或者使用第三方庫(kù)來(lái)支持協(xié)
    的頭像 發(fā)表于 05-30 16:26 ?742次閱讀

    FreeRTOS任務(wù)與協(xié)介紹

    協(xié)是為那些資源很少的 MCU 準(zhǔn)備的,其開(kāi)銷(xiāo)很小,但是 FreeRTOS 官方已經(jīng)不打算再更新協(xié)了。 任務(wù)特性: 1、簡(jiǎn)單。 2、沒(méi)
    的頭像 發(fā)表于 09-28 11:02 ?1040次閱讀

    C++20無(wú)棧協(xié)超輕量高性能異步庫(kù)開(kāi)發(fā)實(shí)戰(zhàn)

    來(lái)了,c++標(biāo)準(zhǔn)委員會(huì)的謹(jǐn)慎態(tài)度也造就了c++20的給出來(lái)協(xié):“性能優(yōu)秀”,“開(kāi)發(fā)靈活”和讓人勸退的“門(mén)檻之高”。 不過(guò)話(huà)說(shuō)回來(lái),c++從出身就注定了背負(fù)性能使命,他不是為簡(jiǎn)單為
    的頭像 發(fā)表于 11-09 10:20 ?1490次閱讀

    協(xié)的實(shí)現(xiàn)與原理

    前言 協(xié)這個(gè)概念很久了,好多程序員是實(shí)現(xiàn)過(guò)這個(gè)組件的,網(wǎng)上關(guān)于協(xié)的文章,博客,論壇都是汗牛充棟,在知乎,github上面也有很多大牛寫(xiě)了關(guān)于協(xié)
    的頭像 發(fā)表于 11-10 10:57 ?476次閱讀

    Linux線(xiàn)程、線(xiàn)程與異步編程、協(xié)與異步介紹

    協(xié)不是系統(tǒng)級(jí)線(xiàn)程,很多時(shí)候協(xié)被稱(chēng)為“輕量級(jí)線(xiàn)程”、“微線(xiàn)程”、“纖(fiber)”等。簡(jiǎn)單來(lái)說(shuō)可以認(rèn)為
    的頭像 發(fā)表于 11-11 11:35 ?1279次閱讀
    Linux線(xiàn)程、線(xiàn)程與異步編程、<b class='flag-5'>協(xié)</b><b class='flag-5'>程</b>與異步介紹
    老虎机批发| 大发888网页登录| 百家乐网络真人斗地主| 百家乐平台租用| 百家乐官网永利赌场娱乐网规则| 澳门百家乐官网先赢后输| 宜城市| 皇家娱乐场| 大发888网页版官网| 威尼斯人娱乐平台反水| 财富百家乐的玩法技巧和规则 | 百家乐闲庄概率| 百家乐官网园是真的不| 包赢百家乐官网的玩法技巧和规则 | 百家乐优博u2bet| 百家乐玩法有技巧| 百家乐官网倍投| 帝王百家乐官网的玩法技巧和规则 | 足球百家乐系统| 卢克索百家乐的玩法技巧和规则| 网上百家乐娱乐平台| 百家乐如何洗吗| 百家乐澳门色子| 视频百家乐试玩| 娱乐城百家乐可以代理吗 | 百家乐官网9点直赢| 澳门百家乐官网必胜看路| 百家乐官网代理每周返佣| 百家乐官网视频连连看| 娱乐城百家乐官网规则| 至尊百家乐官网2012| 百家乐官网真人游戏网上投注 | 冠通网络棋牌世界| 德州扑克发牌员| bet365.com| 真人博彩| 赛马会娱乐城| 波密县| 利记百家乐官网现金网| 网上百家乐官网靠谱吗| 百家乐官网的规则博彩正网|