協(xié)程的概念
協(xié)程就是 Kotlin 提供的一套線程封裝的 API砰左,使用協(xié)程可以讓多線程之間的通信更加簡(jiǎn)單箭券≡驶總之一句話(huà)井联,協(xié)程可以簡(jiǎn)化異步編程卜壕。
舉個(gè)例子:在IO線程發(fā)起網(wǎng)絡(luò)請(qǐng)求,在主線程更新UI烙常。
如果使用 Retrofit +回調(diào)的方式轴捎,代碼類(lèi)似下面這樣
private fun normalRequest() {
apiService.getWxarticle().enqueue(object : retrofit2.Callback<WxArticleResponse> {
override fun onFailure(call: Call<WxArticleResponse>, t: Throwable) {
//請(qǐng)求失敗
}
override fun onResponse(call: Call<WxArticleResponse>, response: retrofit2.Response<WxArticleResponse>) {
//請(qǐng)求成功鹤盒,更新UI
}
})
}
如果使用 Retrofit +協(xié)程的方式,代碼類(lèi)似下面這樣
private fun coroutineRequest2_6() {
launch(Dispatchers.Main) {//在主線程啟動(dòng)協(xié)程
val response = apiService.getWxarticle2()//在后臺(tái)線程發(fā)起網(wǎng)絡(luò)請(qǐng)求
tvResult.text = sb.toString()//在主線程更新UI
}
}
對(duì)比使用Retrofit+回調(diào)的方式侦副,使用協(xié)程代碼明顯更加簡(jiǎn)潔清晰侦锯。
關(guān)于使用協(xié)程請(qǐng)求網(wǎng)絡(luò)請(qǐng)參考 Kotlin協(xié)程請(qǐng)求網(wǎng)絡(luò)。
協(xié)程的掛起
看一個(gè)例子
private fun invokeMethod() {
//注釋1處秦驯,在主線程啟動(dòng)協(xié)程
scope.launch(Dispatchers.Main) {
Log.i(TAG, "getString: current thread " + Thread.currentThread().name)
val result = getString()//注釋2處尺碰,調(diào)用掛起函數(shù)
etText.setText(result)//注釋3處
}
}
//掛起函數(shù)
private suspend fun getString(): String {
//切換到IO線程
return withContext(Dispatchers.IO) {
Log.i(TAG, "getString: current thread " + Thread.currentThread().name)
"empty string"
}
}
輸出結(jié)果
getString: current thread main
getString: current thread DefaultDispatcher-worker-2
注釋1處,在主線程啟動(dòng)一個(gè)協(xié)程译隘,就是指花括號(hào)里的這段代碼亲桥,scope.launch(Dispatchers.Main) {...}
。
注釋2處固耘,當(dāng)協(xié)程運(yùn)行到掛起函數(shù)的時(shí)候题篷,這個(gè)協(xié)程會(huì)被掛起。什么意思呢厅目?
從掛起函數(shù)
getString()
的第一個(gè)掛起點(diǎn)開(kāi)始當(dāng)前線程(在這個(gè)例子中是主線程)不再運(yùn)行協(xié)程中的代碼了番枚。第一個(gè)掛起點(diǎn):可以暫時(shí)理解為withContext(Dispatchers.IO){...}
花括號(hào)里面的代碼塊。掛起函數(shù)執(zhí)行完畢以后璧瞬,協(xié)程從掛起點(diǎn)恢復(fù)户辫,重新切回到當(dāng)前線程(在這個(gè)例子中是主線程)繼續(xù)執(zhí)行協(xié)程中的代碼。
etText.setText(result)
協(xié)程的掛起是非阻塞的是什么意思呢嗤锉?
這個(gè)其實(shí)非常非常簡(jiǎn)單渔欢,沒(méi)有什么高深的東西∥脸溃看代碼奥额。
- 阻塞的例子。
//在主線程執(zhí)行
btnSuspend.setOnClickListener {
tvText1.text = getStringSuspend()
tvText2.text = "興百姓苦访诱,亡百姓苦"
}
/**
* 模擬耗時(shí)操作垫挨,2秒后返回字符串
*/
private fun getStringSuspend(): String {
//讓主線程睡兩秒鐘,其實(shí)就是阻塞了主線程
Thread.sleep(2000)
return "峰巒如聚触菜,波濤如怒九榔,山河表里潼關(guān)路,望西都意躊躇涡相,傷心秦漢經(jīng)行處哲泊,宮闕萬(wàn)間都做了土。"
}
在上面的例子中催蝗,tvText1獲取顯示字符串是一個(gè)耗時(shí)操作切威。注釋1處,tvText2顯示text肯定是在tvText1顯示之后丙号。也就是說(shuō)當(dāng)前線程:主線程 被阻塞了先朦。當(dāng)前線程被阻塞以后當(dāng)前線程的后面的代碼是無(wú)法執(zhí)行的缰冤,必須等待阻塞結(jié)束。
- 協(xié)程的非阻塞
//在主線程執(zhí)行
private var scope: CoroutineScope = CoroutineScope(Dispatchers.Main)
btnUnSuspend.setOnClickListener {
scope.launch {
tvText1.text = getStringUnSuspend()
}
//注釋1處注意:這個(gè)代碼一定要卸載協(xié)程的代碼之外
tvText2.text = "興百姓苦喳魏,亡百姓苦"
}
/**
* 模擬耗時(shí)操作棉浸,2秒后返回字符串。
*/
private suspend fun getStringUnSuspend(): String {
delay(2000)
return "峰巒如聚截酷,波濤如怒涮拗,山河表里潼關(guān)路乾戏,望西都意躊躇迂苛,傷心秦漢經(jīng)行處,宮闕萬(wàn)間都做了土鼓择。"
}
現(xiàn)在三幻,tvText2先顯示text,然后2秒后tvText1才顯示了文字呐能。這里主線程沒(méi)有沒(méi)阻塞念搬,協(xié)程的delay(2000)
,實(shí)際上是在后臺(tái)線程阻塞(延遲了)2秒摆出,阻塞的是后臺(tái)線程朗徊。
注釋1處注意:這個(gè)代碼一定要協(xié)程的代碼之外。如果也寫(xiě)在launch的大括號(hào)之內(nèi)了偎漫,那么tvText2也是在tvText1顯示文字之后再顯示爷恳。這也說(shuō)明了在協(xié)程內(nèi)部,代碼也是順序執(zhí)行的象踊,一個(gè)父協(xié)程會(huì)等待子協(xié)程執(zhí)行完畢之后再繼續(xù)往下執(zhí)行温亲。
scope.launch {
tvText1.text = getStringUnSuspend()
tvText2.text = "興百姓苦,亡百姓苦"
}
/**
* 模擬耗時(shí)操作杯矩,2秒后返回字符串栈虚。正確的的非阻塞,不阻塞當(dāng)前線程史隆,阻塞后臺(tái)線程魂务。
*/
private suspend fun getStringUnSuspend(): String {
delay(2000)
return "峰巒如聚,波濤如怒泌射,山河表里潼關(guān)路粘姜,望西都意躊躇,傷心秦漢經(jīng)行處魄幕,宮闕萬(wàn)間都做了土相艇。"
}
結(jié)論:非阻塞式掛起,就是說(shuō)掛起函數(shù)切到指定的線程執(zhí)行去了纯陨,執(zhí)行完畢以后會(huì)再切回到當(dāng)前線程繼續(xù)執(zhí)行協(xié)程中的代碼坛芽。在掛起函數(shù)切到別的線程執(zhí)行的這段期間留储,當(dāng)前線程是可以繼續(xù)運(yùn)行其他代碼的。
關(guān)于協(xié)程的掛起記住一句話(huà)就行:掛起函數(shù)掛起協(xié)程時(shí)咙轩,不會(huì)阻塞協(xié)程所在的線程获讳。
參考鏈接:
- Kotlin 的協(xié)程用力瞥一眼 - 學(xué)不會(huì)協(xié)程?很可能因?yàn)槟憧催^(guò)的教程都是錯(cuò)的
- Kotlin 協(xié)程的掛起好神奇好難懂活喊?今天我把它的皮給扒了
- 到底什么是「非阻塞式」掛起丐膝?協(xié)程真的更輕量級(jí)嗎?
- Kotlin Coroutines(協(xié)程) 完全解析(一)钾菊,協(xié)程簡(jiǎn)介
- Kotlin Coroutines(協(xié)程) 完全解析(二)帅矗,深入理解協(xié)程的掛起、恢復(fù)與調(diào)度
- Kotlin協(xié)程請(qǐng)求網(wǎng)絡(luò)