成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

What? 你還不知道Kotlin Coroutine?

yunhao / 1484人閱讀

摘要:例如,在方面它主要能夠幫助你解決以下兩個(gè)問(wèn)題在主線程中執(zhí)行耗時(shí)任務(wù)導(dǎo)致的主線程阻塞,從而使發(fā)生。提供主線程安全,同時(shí)對(duì)來(lái)自于主線程的網(wǎng)絡(luò)回調(diào)磁盤(pán)操提供保障。在線程通過(guò)從數(shù)據(jù)庫(kù)取數(shù)據(jù),一旦數(shù)據(jù)返回,在主線程進(jìn)行處理。

今天我們來(lái)聊聊Kotlin Coroutine,如果你還沒(méi)有了解過(guò),那么我要提前恭喜你,因?yàn)槟銓⒄莆找粋€(gè)新技能,對(duì)你的代碼方面的提升將是很好的助力。

What Coroutine

簡(jiǎn)單的來(lái)說(shuō),Coroutine是一個(gè)并發(fā)的設(shè)計(jì)模式,你能通過(guò)它使用更簡(jiǎn)潔的代碼來(lái)解決異步問(wèn)題。

例如,在Android方面它主要能夠幫助你解決以下兩個(gè)問(wèn)題:

在主線程中執(zhí)行耗時(shí)任務(wù)導(dǎo)致的主線程阻塞,從而使App發(fā)生ANR。

提供主線程安全,同時(shí)對(duì)來(lái)自于主線程的網(wǎng)絡(luò)回調(diào)、磁盤(pán)操提供保障。

這些問(wèn)題,在接下來(lái)的文章中我都會(huì)給出解決的示例。

Callback

說(shuō)到異步問(wèn)題,我們先來(lái)看下我們常規(guī)的異步處理方式。首先第一種是最基本的callback方式。

callback的好處是使用起來(lái)簡(jiǎn)單,但你在使用的過(guò)程中可能會(huì)遇到如下情形

        GatheringVoiceSettingRepository.getInstance().getGeneralSettings(RequestLanguage::class.java)
                .observe(this, { language ->
                    convertResult(language, { enable -> 
                        // todo something
                    })
                })

這種在其中一個(gè)callback中回調(diào)另一個(gè)callback回調(diào),甚至更多的callback都是可能存在。這些情況導(dǎo)致的問(wèn)題是代碼間的嵌套層級(jí)太深,導(dǎo)致邏輯嵌套復(fù)雜,后續(xù)的維護(hù)成本也要提高,這不是我們所要看到的。

那么有什么方法能夠解決呢?當(dāng)然有,其中的一種解決方法就是我接下來(lái)要說(shuō)的第二種方式。

Rx系列

對(duì)多嵌套回調(diào),Rx系列在這方面處理的已經(jīng)非常好了,例如RxJava。下面我們來(lái)看一下RxJava的解決案例

        disposable = createCall().map {
            // return RequestType
        }.subscribeWith(object : SMDefaultDisposableObserver{
            override fun onNext(t: RequestType) {
                // todo something
            }
        })

RxJava豐富的操作符,再結(jié)合Observable與Subscribe能夠很好的解決異步嵌套回調(diào)問(wèn)題。但是它的使用成本就相對(duì)提高了,你要對(duì)它的操作符要非常了解,避免在使用過(guò)程中濫用或者過(guò)度使用,這樣自然復(fù)雜度就提升了。

那么我們渴望的解決方案是能夠更加簡(jiǎn)單、全面與健壯,而我們今天的主題Coroutine就能夠達(dá)到這種效果。

Coroutine在Kotlin中的基本要點(diǎn)

在Android里,我們都知道網(wǎng)絡(luò)請(qǐng)求應(yīng)該放到子線程中,相應(yīng)的回調(diào)處理一般都是在主線程,即ui線程。正常的寫(xiě)法就不多說(shuō)了,那么使用Coroutine又該是怎么樣的呢?請(qǐng)看下面代碼示例:

    private suspend fun get(url: String) = withContext(Dispatchers.IO) {
        // to do network request
        url
    }
 
    private suspend fun fetch() { // 在Main中調(diào)用
        val result = get("https://rousetime.com") // 在IO中調(diào)用
        showToast(result) // 在Main中調(diào)用
    }

如果fetch方法在主線程調(diào)用,那么你會(huì)發(fā)現(xiàn)使用Coroutine來(lái)處理異步回調(diào)就像是在處理同步回調(diào)一樣,簡(jiǎn)潔明了、行云流水,同時(shí)再也沒(méi)有嵌套的邏輯了。

注意看方法,Coroutine為了能夠?qū)崿F(xiàn)這種簡(jiǎn)單的操作,增加了兩個(gè)操作來(lái)解決耗時(shí)任務(wù),分別為suspend與resume

suspend: 掛起當(dāng)前執(zhí)行的協(xié)同程序,并且保存此刻的所有本地變量

resume: 從它被掛起的位置繼續(xù)執(zhí)行,并且掛起時(shí)保存的數(shù)據(jù)也被還原

解釋的有點(diǎn)生硬,簡(jiǎn)單的來(lái)說(shuō)就是suspend可以將該任務(wù)掛起,使它暫時(shí)不在調(diào)用的線程中,以至于當(dāng)前線程可以繼續(xù)執(zhí)行別的任務(wù),一旦被掛起的任務(wù)已經(jīng)執(zhí)行完畢,那么就會(huì)通過(guò)resume將其重新插入到當(dāng)前線程中。

所以上面的示例展示的是,當(dāng)get還在請(qǐng)求的時(shí)候,fetch方法將會(huì)被掛起,直到get結(jié)束,此時(shí)才會(huì)插入到主線程中并返回結(jié)果。

一圖勝千言,我做了一張圖,希望能有所幫助。

另外需要注意的是,suspend方法只能夠被其它的suspend方法調(diào)用或者被一個(gè)coroutine調(diào)用,例如launch。

Dispatchers

另一方面Coroutine使用Dispatchers來(lái)負(fù)責(zé)調(diào)度協(xié)調(diào)程序執(zhí)行的線程,這一點(diǎn)與RxJava的schedules有點(diǎn)類(lèi)似,但不同的是Coroutine一定要執(zhí)行在Dispatchers調(diào)度中,因?yàn)镈ispatchers將負(fù)責(zé)resume被suspend的任務(wù)。

Dispatchers提供三種模式切換,分別為

Dispatchers.Main: 使Coroutine運(yùn)行中主線程,以便UI操作

Dispatchers.IO: 使Coroutine運(yùn)行在IO線程,以便執(zhí)行網(wǎng)絡(luò)或者I/O操作

Dispatchers.Default: 在主線程之外提高對(duì)CPU的利用率,例如對(duì)list的排序或者JSON的解析。

再來(lái)看上面的示例

    private suspend fun get(url: String) = withContext(Dispatchers.IO) {
        // to do network request
        url
    }
 
    private suspend fun fetch() { // 在Main中調(diào)用
        val result = get("https://rousetime.com") // 在IO中調(diào)用
        showToast(result) // 在Main中調(diào)用
    }

為了讓get操作運(yùn)行在IO線程,我們使用withContext方法,對(duì)該方法傳入Dispatchers.IO,使得它閉包下的任務(wù)都處于IO線程中,同時(shí)witchContext也是一個(gè)suspend函數(shù)。

創(chuàng)建Coroutine

上面提到suspend函數(shù)只能在相應(yīng)的suspend中或者Coroutine中調(diào)用。那么Coroutine又該如何創(chuàng)建呢?

有兩種方式,分別為launch與async

launch: 開(kāi)啟一個(gè)新的Coroutine,但不返回結(jié)果

async: 開(kāi)啟一個(gè)新的Coroutine,但返回結(jié)果

還是上面的例子,如果我們需要執(zhí)行fetch方法,可以使用launch創(chuàng)建一個(gè)Coroutine

    private fun excute() {
        CoroutineScope(Dispatchers.Main).launch {
            fetch()
        }
    }

另一種async,因?yàn)樗祷亟Y(jié)果,如果要等所有async執(zhí)行完畢,可以使用await或者awaitAll

    private suspend fun fetchAll() {
        coroutineScope {
            val deferredFirst = async { get("first") }
            val deferredSecond = async { get("second") }
            deferredFirst.await()
            deferredSecond.await()

//            val deferred = listOf(
//                    async { get("first") },
//                    async { get("second") }
//            )
//            deferred.awaitAll()
        }
    }

所以通過(guò)await或者awaitAll可以保證所有async完成之后再進(jìn)行resume調(diào)用。

Architecture Components

如果你使用了Architecture Component,那么你也可以在其基礎(chǔ)上使用Coroutine,因?yàn)镵otlin Coroutine已經(jīng)提供了相應(yīng)的api并且定制了CoroutineScope。

如果你還不了解Architecture Component,強(qiáng)烈推薦你閱讀我的Android Architecture Components 系列

在使用之前,需要更新architecture component的依賴(lài)版本,如下所示

object Versions {
    const val arch_version = "2.2.0-alpha01"
    const val arch_room_version = "2.1.0-rc01"
}
 
object Dependencies {
    val arch_lifecycle = "androidx.lifecycle:lifecycle-extensions:${Versions.arch_version}"
    val arch_viewmodel = "androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.arch_version}"
    val arch_livedata = "androidx.lifecycle:lifecycle-livedata-ktx:${Versions.arch_version}"
    val arch_runtime = "androidx.lifecycle:lifecycle-runtime-ktx:${Versions.arch_version}"
    val arch_room_runtime = "androidx.room:room-runtime:${Versions.arch_room_version}"
    val arch_room_compiler = "androidx.room:room-compiler:${Versions.arch_room_version}"
    val arch_room = "androidx.room:room-ktx:${Versions.arch_room_version}"
}
ViewModelScope

在ViewModel中,為了能夠使用Coroutine提供了viewModelScope.launch,同時(shí)一旦ViewModel被清除,對(duì)應(yīng)的Coroutine也會(huì)自動(dòng)取消。

    fun getAll() {
        viewModelScope.launch {
            val articleList = withContext(Dispatchers.IO) {
                articleDao.getAll()
            }
            adapter.clear()
            adapter.addAllData(articleList)
        }
    }

在IO線程通過(guò)articleDao從數(shù)據(jù)庫(kù)取數(shù)據(jù),一旦數(shù)據(jù)返回,在主線程進(jìn)行處理。如果在取數(shù)據(jù)的過(guò)程中ViewModel已經(jīng)清除了,那么數(shù)據(jù)獲取也會(huì)停止,防止資源的浪費(fèi)。

LifecycleScope

對(duì)于Lifecycle,提供了LifecycleScope,我們可以直接通過(guò)launch來(lái)創(chuàng)建Coroutine

    private fun coroutine() {
        lifecycleScope.launch {
            delay(2000)
            showToast("coroutine first")
            delay(2000)
            showToast("coroutine second")
        }
    }

因?yàn)長(zhǎng)ifecycle是可以感知組件的生命周期的,所以一旦組件onDestroy了,相應(yīng)的LifecycleScope.launch閉包中的調(diào)用也將取消停止。

lifecycleScope本質(zhì)是Lifecycle.coroutineScope

val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
    get() = lifecycle.coroutineScope
 
    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
            lifecycle.removeObserver(this)
            coroutineContext.cancel()
        }
    }

它會(huì)在onStateChanged中監(jiān)聽(tīng)DESTROYED狀態(tài),同時(shí)調(diào)用cancel取消Coroutine。

另一方面,lifecycleScope還可以根據(jù)Lifecycle不同的生命狀態(tài)進(jìn)行suspend處理。例如對(duì)它的STARTED進(jìn)行特殊處理

    private fun coroutine() {
        lifecycleScope.launchWhenStarted {

        }
        lifecycleScope.launch {
            whenStarted {  }
            delay(2000)
            showToast("coroutine first")
            delay(2000)
            showToast("coroutine second")
        }
    }

不管是直接調(diào)用launchWhenStarted還是在launch中調(diào)用whenStarted都能達(dá)到同樣的效果。

LiveData

LiveData中可以直接使用liveData,在它的參數(shù)中會(huì)調(diào)用一個(gè)suspend函數(shù),同時(shí)會(huì)返回LiveData對(duì)象

fun  liveData(
    context: CoroutineContext = EmptyCoroutineContext,
    timeoutInMs: Long = DEFAULT_TIMEOUT,
    @BuilderInference block: suspend LiveDataScope.() -> Unit
): LiveData = CoroutineLiveData(context, timeoutInMs, block)

所以我們可以直接使用liveData來(lái)是實(shí)現(xiàn)Coroutine效果,我們來(lái)看下面一段代碼

    // Room
    @Query("SELECT * FROM article_model WHERE title = :title LIMIT 1")
    fun findByTitle(title: String): ArticleModel?
    // ViewModel
    fun findByTitle(title: String) = liveData(Dispatchers.IO) {
        MyApp.db.articleDao().findByTitle(title)?.let {
            emit(it)
        }
    }
    // Activity
    private fun checkArticle() {
        vm.findByTitle("Android Architecture Components Part1:Room").observe(this, Observer {
        })
    }

通過(guò)title從數(shù)據(jù)庫(kù)中取數(shù)據(jù),數(shù)據(jù)的獲取發(fā)生在IO線程,一旦數(shù)據(jù)返回,再通過(guò)emit方法將返回的數(shù)據(jù)發(fā)送出去。所以在View層,我們可以直接使用checkArticle中的方法來(lái)監(jiān)聽(tīng)數(shù)據(jù)的狀態(tài)。

另一方面LiveData有它的active與inactive狀態(tài),對(duì)于Coroutine也會(huì)進(jìn)行相應(yīng)的激活與取消。對(duì)于激活,如果它已經(jīng)完成了或者非正常的取消,例如拋出CancelationException異常,此時(shí)將不會(huì)自動(dòng)激活。

對(duì)于發(fā)送數(shù)據(jù),還可以使用emitSource,它與emit共同點(diǎn)是在發(fā)送新的數(shù)據(jù)之前都會(huì)將原數(shù)據(jù)清除,而不同點(diǎn)是,emitSource會(huì)返回一個(gè)DisposableHandle對(duì)象,以便可以調(diào)用它的dispose方法進(jìn)行取消發(fā)送。

最后我使用Architecture Component與Coroutine寫(xiě)了個(gè)簡(jiǎn)單的Demo,大家可以在Github中進(jìn)行查看

源碼地址: https://github.com/idisfkj/an...

推薦閱讀

Android Architecture Components Part1:Room

Android Architecture Components Part2:LiveData

Android Architecture Components Part3:Lifecycle

Android Architecture Components Part4:ViewModel

公眾號(hào)

掃描二維碼,關(guān)注微信公眾號(hào),獲取獨(dú)家最新IT技術(shù)!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/75042.html

相關(guān)文章

  • 作為前端開(kāi)發(fā)者,還不知道什么是 postCss?

    摘要:的定位屬于預(yù)處理器嗎還是屬于后置處理器都不是,因?yàn)榫唧w做的事取決于開(kāi)發(fā)者使用了什么插件。這里做一個(gè)我覺(jué)得比較恰當(dāng)?shù)念?lèi)比,中的相當(dāng)于的中的,,等預(yù)處理器相當(dāng)于,雖然不是完全合理,但是還是比較恰當(dāng)。 前言 原諒我取這樣的標(biāo)題,我知道 postCss 對(duì)于大多數(shù)前端開(kāi)發(fā)者來(lái)說(shuō)早已經(jīng)很熟悉了,但是樓主作為一個(gè)初出茅廬的前端er,還有好多的工具和技術(shù)沒(méi)接觸過(guò),說(shuō)來(lái)慚愧。雖然平時(shí)也喜歡使用css預(yù)...

    appetizerio 評(píng)論0 收藏0
  • 【Bugly 技術(shù)干貨】Android開(kāi)發(fā)必備知識(shí):為什么說(shuō)Kotlin值得一試

    摘要:的空安全設(shè)計(jì),主要是在類(lèi)型后面加表示可空,否則就不能為。換句話說(shuō),這里的提供了初始化的方法,不過(guò)真正初始化這個(gè)動(dòng)作發(fā)生的時(shí)機(jī)卻是在第一次被使用時(shí)了。至于技術(shù),實(shí)際上是的一個(gè)應(yīng)用,也就是屬性代理了。 1、Hello, Kotlin Bugly 技術(shù)干貨系列內(nèi)容主要涉及移動(dòng)開(kāi)發(fā)方向,是由 Bugly 邀請(qǐng)騰訊內(nèi)部各位技術(shù)大咖,通過(guò)日常工作經(jīng)驗(yàn)的總結(jié)以及感悟撰寫(xiě)而成,內(nèi)容均屬原創(chuàng),轉(zhuǎn)載請(qǐng)標(biāo)明...

    hizengzeng 評(píng)論0 收藏0
  • webpack 大法好 ---- what`s webpack?(前言)

    摘要:原始的開(kāi)發(fā)模式已經(jīng)滿足不了呈指數(shù)增長(zhǎng)的需求了。它承擔(dān)起了模塊管理這一重要角色。是個(gè)前端小菜鳥(niǎo),接觸前端不到兩年時(shí)間,去年畢業(yè)正式參加工作。目前就職于杭州邊鋒網(wǎng)絡(luò)神盾局就是這么霸氣。 對(duì)于剛進(jìn)入前端領(lǐng)域的人,特別是還處于小白階段的初學(xué)者來(lái)說(shuō),很多人對(duì) webpack 并不熟知。就像 Light (對(duì),我就是 Light)一樣,剛接觸前端,最關(guān)心的就是樣式和簡(jiǎn)單的交互了。那時(shí)候怎么會(huì)知道像...

    wwolf 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<