Android的協(xié)程(Coroutines)基礎(chǔ)概念

最近學(xué)習(xí)了下協(xié)程,在這里分享一下我自己的理解。

1、協(xié)程是什么?

在定義協(xié)程是什么之前,我們應(yīng)該先知道協(xié)程是做什么的,對(duì)于項(xiàng)目而言是新增加的東西?還是替換原有的邏輯,從而獲得好處?
我們先看看coroutines最基礎(chǔ)的用法:

    private fun runCoroutines() {
        GlobalScope.launch(Dispatchers.Main) {
            val data = getData()//獲取網(wǎng)絡(luò)接口數(shù)據(jù)
            val processedData = processData(data)//處理數(shù)據(jù)
            textView.text = processedData//UI顯示數(shù)據(jù)
        }
    }

    private suspend fun getData(): String {
        return withContext(Dispatchers.IO) {
            "hen_coder"
        }
    }

    private suspend fun processData(data: String): String {
        return withContext(Dispatchers.IO) {
            data.split("_")//把"hen_coder" 拆成 ["hen","coder"]
                .map { it.capitalize() }//把["hen","coder"]改成["Hen","Coder"]
                .reduce { acc, s -> acc + s }//把["Hen","Coder"]改成HenCoder
        }
    }

launch就是協(xié)程的啟動(dòng)函數(shù),可以理解為創(chuàng)建一個(gè)協(xié)程。
然后協(xié)程內(nèi)部有3步操作,分別是從網(wǎng)絡(luò)接口獲取數(shù)據(jù);處理數(shù)據(jù);UI顯示。
根據(jù)以前的知識(shí),獲取接口數(shù)據(jù)是耗時(shí)操作,是需要異線程處理的;而處理數(shù)據(jù),也有可能是耗時(shí)操作,我們需要根據(jù)情況來(lái)決定是否使用異線程來(lái)處理;而最后的UI展示數(shù)據(jù),是一定要在主線程中處理的。
如果使用retrofit+rxjava來(lái)實(shí)現(xiàn)的話,就是異線程執(zhí)行耗時(shí)邏輯,然后通過(guò)回調(diào)方法,來(lái)通知UI界面刷新數(shù)據(jù)。
但是,在協(xié)程的代碼中,我們看到了,并沒(méi)有用到新建線程和回調(diào)之類的東西,但是代碼運(yùn)行起來(lái)是完全沒(méi)有問(wèn)題的,那原因就只有一種,就是協(xié)程幫我們把這些事情都完成了。
細(xì)心的還可以發(fā)現(xiàn),不只是接口請(qǐng)求,甚至一些耗時(shí)操作,協(xié)程都可以幫你切換到異線程執(zhí)行,成功后切回主線程。
所以,我們暫時(shí)可以定義協(xié)程為:處理多任務(wù)并發(fā)的手段,最大的特點(diǎn)就是可以自動(dòng)幫助我們切換線程

2、協(xié)程怎么用?

首先,導(dǎo)入coroutines

    // Coroutines
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2'

然后就可以在代碼中使用協(xié)程了,創(chuàng)建協(xié)程就是上面的代碼:

GlobalScope.launch(Dispatchers.Main) {

        }

這里的代碼需要注意的是,GlobalScope一般是調(diào)試測(cè)試的時(shí)候使用,正常項(xiàng)目不用這個(gè),但是這里只是演示基礎(chǔ),不要緊。launch就是啟動(dòng)一個(gè)協(xié)程,而Dispatchers.Main就是指定協(xié)程內(nèi)的代碼,運(yùn)行在主線程中。
上面的定義我說(shuō),協(xié)程可以自動(dòng)切換線程,而launch中的代碼又被指定在主線程中運(yùn)行了,那耗時(shí)操作怎么辦?怎么切換到其他線程?

    private suspend fun getData(): String {
        return withContext(Dispatchers.IO) {
            "hen_coder"
        }
    }

這里的代碼涉及到2個(gè)新東西,第一個(gè)是suspend修飾符,另一個(gè)是withContext函數(shù)。
suspend的作用就是標(biāo)志方法為掛起函數(shù),被修飾為掛起函數(shù)的函數(shù),只能在協(xié)程或者其他掛起函數(shù)中調(diào)用。
withContext函數(shù)的作用就是用來(lái)切換線程,后面可以看到Dispatchers.IO,就是我切換到IO線程了。
這樣,掛機(jī)函數(shù)就可以切到其他線程來(lái)執(zhí)行了,執(zhí)行完又回到launch中的主線程中繼續(xù)執(zhí)行,達(dá)到了自動(dòng)切換線程效果。

為什么要用協(xié)程?跟普通的線程有什么優(yōu)缺點(diǎn)?

協(xié)程簡(jiǎn)潔了很多,省略了普通線程的回調(diào),使得代碼看起來(lái)更加好理解。
線程間的切換,我們只需要指定dispatch來(lái)指定就可以了。
缺點(diǎn):性能差一絲絲,因?yàn)閟uspend函數(shù),對(duì)于自動(dòng)尋回切換線程的操作,其實(shí)是相對(duì)復(fù)雜的,但這一點(diǎn)性能的損耗,相比起協(xié)程的優(yōu)點(diǎn)來(lái)說(shuō),可以忽略。

協(xié)程是輕量級(jí)的線程?

看情況。
協(xié)程不只是給Android使用的,如果從Android的角度來(lái)說(shuō),kotlin的協(xié)程,并不是輕量級(jí)的線程。為什么?因?yàn)锳ndroid是基于JVM的,JVM能識(shí)別線程,但識(shí)別不了真正的協(xié)程。那為什么能用呢?那是因?yàn)锳ndroid中的協(xié)程,最終還是用線程。所以不能說(shuō)是輕量級(jí)的線程。
非Android使用的協(xié)程呢,有些的確是的,可以看協(xié)程的官方文檔。

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

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