協(xié)程(coroutines)是一種并發(fā)設(shè)計(jì)模式,您可以在Android 平臺(tái)上使用它來簡化異步執(zhí)行的代碼。協(xié)程是在版本 1.3 中添加到 Kotlin 的,它基于來自其他語言的既定概念。
在 Android 上,協(xié)程有助于管理長時(shí)間運(yùn)行的任務(wù),如果管理不當(dāng),這些任務(wù)可能會(huì)阻塞主線程并導(dǎo)致應(yīng)用無響應(yīng)。使用協(xié)程的專業(yè)開發(fā)者中有超過 50% 的人反映使用協(xié)程提高了工作效率。
為什么協(xié)程如此重要?
協(xié)程是 Kotlin 對比 Java 的最大優(yōu)勢。Java 也在計(jì)劃著實(shí)現(xiàn)自己的協(xié)程:Loom,不過這個(gè)畢竟還處于相當(dāng)初級(jí)的階段。而 Kotlin 的協(xié)程,可以幫我們極大地簡化異步、并發(fā)編程、優(yōu)化軟件架構(gòu)。通過協(xié)程,我們不僅可以提高開發(fā)效率,還能提高代碼的可讀性,由此也就可以降低代碼出錯(cuò)的概率。
協(xié)程基本使用
若使用協(xié)程,首先我們得引入?yún)f(xié)程相關(guān)的開發(fā)包
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7-mpp-dev-11'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7-mpp-dev-11'
?
上面一個(gè)是java環(huán)境的,下面一個(gè)是android環(huán)境下的,這里我們都引入進(jìn)來。
協(xié)程最簡單的使用,我們可以使用GlobalScope.launch去開啟一個(gè)協(xié)程代碼如下所示:
GlobalScope.launch {
Log.d(TAG, Thread.currentThread().name)
}
運(yùn)行結(jié)果如下所示:
說明這段代碼是運(yùn)行在一個(gè)子線程中的,當(dāng)然我們可以在launch中傳遞參數(shù),讓他運(yùn)行在主線程中:
GlobalScope.launch(Dispatchers.Main) {
Log.d(TAG, Thread.currentThread().name)
}
這樣里面的這段代碼就運(yùn)行在主線程中了,由此可見 協(xié)程是什么?簡單的說就是協(xié)程可以讓我們開啟一個(gè)線程,是一個(gè)線程框架。當(dāng)然實(shí)際項(xiàng)目中,開啟協(xié)程我們不會(huì)使用上面的方法。別急,我們要循序漸進(jìn)~
協(xié)程和線程相比有什么優(yōu)勢?
協(xié)程和線程相比的優(yōu)勢,我們直接用一個(gè)例子來說明,比如當(dāng)前我們要去請求網(wǎng)絡(luò),拿到數(shù)據(jù)后將數(shù)據(jù)顯示出來,這里我們模擬兩個(gè)方法,分別為 網(wǎng)絡(luò)請求獲取數(shù)據(jù) 和 將數(shù)據(jù)顯示在UI上,我們定義方法如下:
?
/**
* 從服務(wù)器取信息
*/
private fun getMessageFromNetwork(): String {
?
for (i in 0..1000000) {
//這里模擬一個(gè)耗時(shí)操作
}
?
var name = "Huanglinqing"
return name
}
?
/**
* 顯示信息
* @message :信息
*/
private fun showMessage(message: String) {
tvName.text = message
}
由于getMessage是一個(gè)耗時(shí)操作,所以我們將他放在子線程中,而在Android中 UI更新操作不能放在子線程中,所以我們必須將showMessage方法放在UI線程中,在之前我們實(shí)現(xiàn)這種需求 只能自己去執(zhí)行切線程的代碼,代碼如下所示
?
/**
* 從服務(wù)器取信息
*/
private fun getMessageFromNetwork() {
?
for (i in 0..1000000) {
//這里模擬一個(gè)耗時(shí)操作
}
?
var name = "Huanglinqing"
runOnUiThread {
showMessage(name)
}
}
在onCreate中執(zhí)行如下代碼:
Thread {
getMessageFromNetwork()
}.start()
這樣呢 看起來還好,但是如果現(xiàn)在需求改為:請求第一個(gè) 后 顯示出來,再請求第二個(gè) 顯示出來呢,代碼就是這個(gè)樣子
private fun getMessageFromNetwork() {
?
for (i in 0..1000000) {
//這里模擬一個(gè)耗時(shí)操作
}
var name = "Huanglinqing"
runOnUiThread {
showMessage(name)
Thread{
getMessageFromNetwork1()
runOnUiThread{
}
}.start()
}
}
依次類推,我們可以想到,如果請求很多的話,第一 代碼結(jié)構(gòu)會(huì)很難看,第二 寫著寫著就很亂了,那么協(xié)程就可以很好的解決這個(gè)問題,下面我們來看使用協(xié)程的方式 怎么寫。
首先,對于一個(gè)耗時(shí)的操作,我們需要將他切換到后臺(tái)線程執(zhí)行,withContext函數(shù)可以構(gòu)建一個(gè)協(xié)程作用域,他必須在掛起函數(shù)或者協(xié)程中執(zhí)行,suspend關(guān)鍵字是kotlin為我們提供的 用于標(biāo)記掛起函數(shù)的關(guān)鍵字。我們修改getMessageFromNetwork方法如下:
/**
* 從服務(wù)器取信息
*/
private suspend fun getMessageFromNetwork(): String {
?
var name = ""
withContext(Dispatchers.IO) {
for (i in 0..1000000) {
//這里模擬一個(gè)耗時(shí)操作
}
?
name = "Huanglinqing1111"
}
?
return name
}
在onCreate中協(xié)程中直接這樣寫:
GlobalScope.launch(Dispatchers.Main) {
var name = getMessageFromNetwork()
showMessage(name)
}
運(yùn)行結(jié)果如下所示:
如果我們有多個(gè)請求呢,那就再多加幾個(gè)
GlobalScope.launch(Dispatchers.Main) {
var name = getMessageFromNetwork()
showMessage(name)
var name1 = getMessageFromNetwork()
showMessage(name1)
var name2 = getMessageFromNetwork()
showMessage(name2)
}
這樣getMessageFromNetwork在后臺(tái)執(zhí)行,showMessage在前臺(tái)執(zhí)行,由此看來。
協(xié)程比線程的優(yōu)勢在什么地方?
1、協(xié)程可以幫我們自動(dòng)切線程
2、擺脫了鏈?zhǔn)交卣{(diào)的問題
Retrofit 如何使用協(xié)程
從Retrofit2.6.0開始,retrofit就自動(dòng)支持協(xié)程了,這里我們從「聚合數(shù)據(jù)」上找到一個(gè)開放api;我們先來看之前我們怎么使用的,首先在Apiservice中定義一個(gè)接口如下:
@GET("https://wanandroid.com/article/listproject/0/json")
fun queryData(): Call<BaseReqData<DataReqBean>>
在activity中添加如下代碼:
var retrofit = Retrofit.Builder()
.baseUrl("http://v.juhe.cn/")
.addConverterFactory(GsonConverterFactory.create())
.build()
?
val apiService = retrofit.create(ApiService::class.java)
?
tvName.setOnClickListener {
apiService.queryData("top","04ea095cbea56775e2d1669713f34cc2")
.enqueue(object :Callback<BaseReqData<NewBean>>{
override fun onFailure(call: Call<BaseReqData<NewBean>>, t: Throwable) {
tvName.text = t.toString()
Log.d("網(wǎng)絡(luò)請求錯(cuò)誤", t.toString())
}
?
override fun onResponse(
call: Call<BaseReqData<NewBean>>,
response: Response<BaseReqData<NewBean>>
) {
tvName.text = response.code().toString()
}
})
這里我們將返回結(jié)果的狀態(tài)碼顯示在view上,運(yùn)行結(jié)果如圖所示:
上面代碼看起來沒有什么問題,如果我們用到了mvp模式什么的,便于職責(zé)單一,還要單獨(dú)放一個(gè)類中,這樣就需要添加回調(diào)才能獲取返回結(jié)果。
那么協(xié)程中怎么使用呢?
首先我們在ApiService中新增一個(gè)函數(shù) ,聲明為掛起函數(shù),類型不需要添加Call
@GET("toutiao/index")
suspend fun queryDataKotlin(@Query("type") type: String?, @Query("key") key: String?): BaseReqData<NewBean>
在onCreate中代碼如下所示:
GlobalScope.launch(Dispatchers.Main) {
try {
var result = apiService.queryDataKotlin("top", "04ea095cbea56775e2d1669713f34cc2")
tvName.text = result.toString()
}catch (e:Exception){
tvName.text = e.toString()
}
}
沒錯(cuò)就是這么簡單,沒有什么回調(diào),因?yàn)閝ueryDataKotlin是一個(gè)掛起函數(shù),當(dāng)運(yùn)行到掛起函數(shù)的時(shí)候,協(xié)程會(huì)處于等待狀態(tài),等返回結(jié)果后,主動(dòng)切回主線程,執(zhí)行下面的方法。
而try catch的作用,就等同于上面onFailure的回調(diào),這個(gè)時(shí)候你可能會(huì)說了,我去!還要寫try catch ,好low的感覺,別忘了,協(xié)程的另一個(gè)優(yōu)勢就是可以減少回調(diào),如果仍然有成功方法或者失敗方法 那還是走了回調(diào)的邏輯!
協(xié)程提升效率
協(xié)程可以提升什么效率,假設(shè),我們現(xiàn)在有兩個(gè)接口請求,需要等到兩個(gè)接口都請求完畢的時(shí)候 顯示出結(jié)果,如果在之前會(huì)怎么樣,先請求接口1 接口1有結(jié)果后再請求結(jié)果2,而協(xié)程可以做到,接口1和接口2同時(shí)請求,等請求結(jié)束后將結(jié)果合并顯示過來,這里我們請求統(tǒng)一接口來模擬請求兩個(gè)不同的接口
GlobalScope.launch(Dispatchers.Main) {
try {
var result1 =
async { apiService.queryDataKotlin("top", "04ea095cbea56775e2d1669713f34cc2") }
var result2 =
async { apiService.queryDataKotlin("top", "04ea095cbea56775e2d1669713f34cc2") }
tvName.text = result1.await().toString() + "\n==\n" + result2.await().toString()+"接口2"
} catch (e: Exception) {
tvName.text = e.toString()
}
?
}
運(yùn)行結(jié)果如下所示:
這樣,本來要分步做的兩件事情可以同時(shí)做了,當(dāng)然可以提高效率了,async函數(shù)必須在協(xié)程作用域中調(diào)用,會(huì)創(chuàng)建一個(gè)新的子協(xié)程,并返回一個(gè)Deferred對象,調(diào)用這個(gè)對象的await方法 就可以獲取執(zhí)行結(jié)果。
本文主要解析在Kotlin中的協(xié)程簡單使用以及和線程的對比優(yōu)勢,有關(guān)更多的Kotlin技術(shù),可以參考《Kotlin手冊》學(xué)習(xí)更多核心技術(shù)點(diǎn),可以查看詳細(xì)類目。
文章來源:http://www.zghlxwxcb.cn/news/detail-706981.html
最后
要記住協(xié)程的幾個(gè) API 很容易,困難的是形成一套完整的協(xié)程知識(shí)體系。其實(shí),學(xué)習(xí)協(xié)程,相當(dāng)于一次編程思維的升級(jí)。協(xié)程思維,它與我們常見的線程思維迥然不同,當(dāng)我們能夠用協(xié)程的思維來分析問題以后,線程當(dāng)中某些棘手的問題在協(xié)程面前都會(huì)變成小菜一碟。因此,我們相當(dāng)于多了一種解決問題的手段。文章來源地址http://www.zghlxwxcb.cn/news/detail-706981.html
到了這里,關(guān)于協(xié)程 VS 線程,Kotlin技術(shù)精講的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!