Android開發(fā)中使用協(xié)程(上手指南)

一.保持對協(xié)程的追蹤

我們知道協(xié)程可以處理耗時任務,但是假如我們的項目中開啟了一千個協(xié)程處理任務,我們都是通過手動去追蹤它們,那代碼很容易出錯。也很容易失去對協(xié)程的實時跟蹤逃默。就會引起work leak吟税,任務泄漏异旧,類似內(nèi)存泄漏一樣潮针,任務泄漏會導致CPU焦读,內(nèi)存喧兄,磁盤資源被占用恭理。甚至會發(fā)起一些無用的網(wǎng)絡請求。為了避免任務的泄漏及志,Kotlin引入了結(jié)構(gòu)化并發(fā)機制冶共。
在Android平臺使用結(jié)構(gòu)化并發(fā)可以做到以下三件事:
(1)取消任務——當任務不需要繼續(xù)執(zhí)行的時候篡九,取消他
(2)追蹤任務——當任務正在執(zhí)行的時候追蹤他
(3)發(fā)出錯誤的信號——當協(xié)程執(zhí)行失敗時沛善,發(fā)出錯誤信號表明有錯誤發(fā)生

二.借助scope 來取消任務

scope在協(xié)程中稱為作用域媳友,協(xié)程的運行必須指定作用域赶促,CoroutineScope(作用域)可以對協(xié)程進行追蹤空幻,即使協(xié)程被掛起也是如此但两。CoroutineScope并不運行協(xié)程坊罢,他只是確保實時跟著協(xié)程的運行乖仇。因此在Kotlin中不允許在沒有作用域的情況下啟動新的協(xié)程。作用域可以跟蹤所有的協(xié)程,有可以取消由它啟動的協(xié)程艺骂。在Android開發(fā)中經(jīng)常用到。比如在一個Activity中啟動一個協(xié)程巩检,當Activity關(guān)閉的時候劳跃,取消協(xié)程的執(zhí)行谎仲。這個時候我們可以聯(lián)想到讓我們Activity的生命周期保持和作用域的生命周期一樣。以上總結(jié)就是:作用域(CoroutineScope )會跟蹤所有的協(xié)程刨仑,并可以取消由它啟動的協(xié)程郑诺。

三.協(xié)程的啟動方式

啟動協(xié)程,必須要在作用域內(nèi)杉武,而啟動協(xié)程的方式有兩種:
(1)launch構(gòu)建器——啟動新的協(xié)程而不將結(jié)果返回給調(diào)用方
(2)async構(gòu)建器——啟動新的協(xié)程辙诞,通過await的掛起函數(shù)返回result
如:以下launch啟動的協(xié)程,我們只是在掛起函數(shù)doSomthingOne中拿到了返回值轻抱。

package com.example.kotlin01

import kotlinx.coroutines.*
suspend fun main() {
    runBlocking {
        var job = launch {
            var result1 = doSomthingOne()
            println(result1)
        }

    }

}
suspend fun doSomthingOne(): Int {
    delay(2000)
    return 13
}

13

如:以下async啟動的協(xié)程飞涂,我們通過返回調(diào)用await拿到返回值。

package com.example.kotlin01

import kotlinx.coroutines.*
suspend fun main() {
    runBlocking {
        var job = async {
           doSomthingOne()
        }
        println(job.await())

    }

}

suspend fun doSomthingOne(): Int {
    delay(2000)
    return 13
}
13

四.在 ViewModel 中啟動協(xié)程

項目中經(jīng)常我們通過使用jetpack中的ViewModel組件來作為View的控制器和Activity綁定起來,這樣以來我們知道即使當我們的手機屏幕旋轉(zhuǎn)Activity異常銷毀重建的時候我們的Viewmodel也不會銷毀较店。如果我們在ViewModel中啟動協(xié)程士八,我們的協(xié)程也不會因此而被終止。我們知道協(xié)程都是運行在作用域中的梁呈,所以就產(chǎn)生了我們的ViewModel.viewModelScope婚度。當viewModelScope被清除,調(diào)用onCleared的時候官卡,我們的協(xié)程也就取消執(zhí)行了蝗茁。比如當一個接口正在請求數(shù)據(jù)的時候,此時用戶關(guān)閉了應用寻咒,那么我們的請求就應該被取消掉哮翘。當一個協(xié)程中又啟動了一個新的協(xié)程,那他們都公用父協(xié)程的作用域仔涩。viewModelScope的使用實例

package com.example.kotlin01

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class MyViewModel : ViewModel() {

    fun runForever() {
        // 在 ViewModel 中啟動新的協(xié)程
        viewModelScope.launch {
            // 當 ViewModel 被清除后忍坷,下列代碼也會被取消
            while (true){
                println("你好")
            }
        }
    }
}

我們看到我們在協(xié)程體中寫了一個死循環(huán),但是假如我們的Viewmodel被清除的時候熔脂,協(xié)程也會被取消佩研。死循環(huán)中的代碼也會取消執(zhí)行。

五.任務追蹤

有時候我們會遇到一些更復雜的問題霞揉,比如在一個協(xié)程中處理多個網(wǎng)絡請求旬薯,我們可以作在一個協(xié)程中又啟動多個協(xié)程去執(zhí)行任務,但是如果隨意啟動協(xié)程适秩,就會容易引起任務泄漏绊序,調(diào)用方可能感知不到啟動了新的協(xié)程,也就無法追蹤秽荞。為了解決這個問題骤公,結(jié)構(gòu)化并發(fā)保證了supend函數(shù)返回時,處理任務也都完成扬跋。我們使用coroutineScope或者supervisorScope構(gòu)造器在supend函數(shù)中啟動新的協(xié)程阶捆,確保了supend函數(shù)返回的時候,他們所做的任務也都能結(jié)束钦听。

suspend fun fetchTwoDocs() {
    coroutineScope {
        launch { fetchDoc(1) }
        async { fetchDoc(2) }
    }
}

以上suspend函數(shù)fetchTwoDocs中使用了coroutineScope構(gòu)造器啟動了兩個協(xié)程洒试,當fetchTwoDocs函數(shù)返回的時候,確保了launch和async啟動的協(xié)程也都已經(jīng)完成執(zhí)行朴上。

六.任務追蹤(處理一堆任務)

在以上的例子中垒棋,假如我們在supend函數(shù)中啟動了一千個協(xié)程,那么是否還能正常追蹤呢痪宰,結(jié)果也是一樣的叼架。coroutineScope構(gòu)造器啟動的協(xié)程會等待協(xié)程中的任務執(zhí)行完成畔裕,supend函數(shù)才會返回。并且coroutineScope還會創(chuàng)建一個子scope(作用域)碉碉,我們知道supend函數(shù)肯定是由某個協(xié)程內(nèi)部調(diào)用的柴钻,運行在某個作用域內(nèi),那么suspend函數(shù)中通過coroutineScope啟動的協(xié)程會創(chuàng)建一個子作用域scope 垢粮,這個子作用域繼承了suspend函數(shù)所在協(xié)程的作用域(父作用域)贴届。假如這個父作用域是一個viewScope,當用戶離開界面蜡吧,關(guān)閉Activity的時候毫蚓,viewScope被清除,那么所有的子作用域也會被清楚昔善。子作用域內(nèi)的協(xié)程也會被取消執(zhí)行元潘。這樣就不會造成了任務泄漏。

七.協(xié)程失敗時發(fā)出報錯信號

val unrelatedScope = MainScope()
// 丟失錯誤的例子
suspend fun lostError() {
   // 未使用結(jié)構(gòu)化并發(fā)的 async
    unrelatedScope.async {
        throw InAsyncNoOneCanHearYou("except")
    }
}

以上我們在supend函數(shù)中聲明了一個無關(guān)聯(lián)的作用域君仆,它不會按照結(jié)構(gòu)化并發(fā)的方式啟動新的協(xié)程翩概,這段代碼的錯誤將丟失,因為異常通過去獲取返咱,而我們并沒有調(diào)用await 钥庇。所以這個錯誤將永遠不會得到處理。
使用結(jié)構(gòu)化并發(fā)

suspend fun foundError() {
    coroutineScope {
        async { 
            throw StructuredConcurrencyWill("throw")
        }
    }
}

使用結(jié)構(gòu)化并發(fā)就是在supend函數(shù)中使用coroutineScope構(gòu)造器啟動協(xié)程咖摹,coroutineScope將會創(chuàng)建子作用域繼承了supend函數(shù)所在協(xié)程的作用域评姨。如果coroutineScope創(chuàng)建的協(xié)程拋出了異常,coroutineScope將會拋給調(diào)用方萤晴,因為我們使用的是coroutineScope而不是supervisorScope吐句,當拋出異常的時候,所有的子任務就會被取消店读。

八.理解結(jié)構(gòu)化并發(fā)

package com.example.kotlin01

import kotlinx.coroutines.*

suspend fun main() {
    println("${doSum()}")
}

suspend fun doSum(): Int = coroutineScope {
    var result1 = async { doSomthingOne() }
    var result2 = async { doSomthingTwo() }
    result1.await() + result2.await()
}

suspend fun doSomthingOne(): Int {

    delay(2000)
    println("doSomthingOne")
    return 13
}

suspend fun doSomthingTwo(): Int {
    delay(1000)
    println("doSomthingTwo")
    return 15
}

在supend 函數(shù)中我們通過coroutineScope 構(gòu)造器啟動了兩個協(xié)程調(diào)用doSomthingOne嗦枢,doSomthingTwo,因為coroutineScope 啟動的協(xié)程的作用域都繼承了supend函數(shù)所在協(xié)程的作用域屯断,當doSomthingOne函數(shù)出現(xiàn)異常的時候文虏,會拋給父作用域,父作用域下協(xié)程的的子協(xié)程都會被取消裹纳。也就是doSomthingOne和doSomthingTwo都會被取消。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末紧武,一起剝皮案震驚了整個濱河市剃氧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阻星,老刑警劉巖朋鞍,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件已添,死亡現(xiàn)場離奇詭異,居然都是意外死亡滥酥,警方通過查閱死者的電腦和手機更舞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坎吻,“玉大人缆蝉,你說我怎么就攤上這事∈菡妫” “怎么了刊头?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長诸尽。 經(jīng)常有香客問我原杂,道長,這世上最難降的妖魔是什么您机? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任穿肄,我火速辦了婚禮,結(jié)果婚禮上际看,老公的妹妹穿的比我還像新娘咸产。我一直安慰自己,他們只是感情好仿村,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布锐朴。 她就那樣靜靜地躺著,像睡著了一般蔼囊。 火紅的嫁衣襯著肌膚如雪焚志。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天畏鼓,我揣著相機與錄音酱酬,去河邊找鬼。 笑死云矫,一個胖子當著我的面吹牛膳沽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播让禀,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼挑社,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了巡揍?” 一聲冷哼從身側(cè)響起痛阻,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎腮敌,沒想到半個月后阱当,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體俏扩,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年弊添,在試婚紗的時候發(fā)現(xiàn)自己被綠了录淡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡油坝,死狀恐怖嫉戚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情免钻,我是刑警寧澤彼水,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站极舔,受9級特大地震影響凤覆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拆魏,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一盯桦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渤刃,春花似錦拥峦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至洋闽,卻和暖如春玄柠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背诫舅。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工羽利, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人刊懈。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓这弧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親虚汛。 傳聞我的和親對象是個殘疾皇子匾浪,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容