記錄一下學(xué)習(xí)的知識(shí)
1.什么是協(xié)程?
廣義的協(xié)程是一種在程序中處理并發(fā)任務(wù)的方案; 并且協(xié)程也是這種方案的一個(gè)組件
例如:適配器模式是一種方案, 而項(xiàng)目中又存在具體的XxxAdapter
,也是一個(gè)組件廣義的協(xié)程和線程屬于一個(gè)層級(jí)的概念,屬于并列的關(guān)系
例如:如果需要異步處理任務(wù),你可以選擇線程,也可以選擇協(xié)程
2.什么是Kotlin for Java的協(xié)程?
Kotlin for Java的協(xié)程和廣義的協(xié)程并不相同.
Kotlin for Java的協(xié)程底層是基于線程,是一個(gè)線程框架,最終還是使用線程池來完成異步任務(wù)
3.Android中的協(xié)程代碼怎么寫?
- 用launch來開啟一段協(xié)程
- 把需要放在異步后臺(tái)工作的函數(shù),寫成suspend函數(shù),并且調(diào)用其他suspend函數(shù)來真正切換線程**
- 最簡(jiǎn)單的使用
GlobalScope.launch { ... }
GlobalScope.launch {
showLog("Current Thread name: ${Thread.currentThread().name}")
}
打印結(jié)果: Current Thread name: DefaultDispatcher-worker-1, 這里可以看到不是主線程Main了
下面舉個(gè)例子說明: 比如有幾個(gè)函數(shù)
fun ioCode1(){
showLog("ioCode1 Thread name: ${Thread.currentThread().name}")
}
fun uiCode1(){
showLog("uiCode1 Thread name: ${Thread.currentThread().name}")
}
fun ioCode2(){
showLog("ioCode2 Thread name: ${Thread.currentThread().name}")
}
fun uiCode2(){
showLog("uiCode2 Thread name: ${Thread.currentThread().name}")
}
fun ioCode3(){
showLog("ioCode3 Thread name: ${Thread.currentThread().name}")
}
fun uiCode3(){
showLog("uiCode3 Thread name: ${Thread.currentThread().name}")
}
我們希望ioCode的函數(shù)在異步執(zhí)行,uiCode在主線程執(zhí)行,并且按指定的順序執(zhí)行1,2,3
如果不使用協(xié)程,我們可以這樣寫:
thread {
ioCode1()
runOnUiThread {
uiCode1()
thread {
ioCode2()
runOnUiThread {
uiCode2()
thread {
ioCode3()
runOnUiThread {
uiCode3()
}
}
}
}
}
}
從這個(gè)簡(jiǎn)單的例子可以看出,嵌套層次有點(diǎn)多,如果業(yè)務(wù)邏輯比較復(fù)雜,不利于后期維護(hù)和修改
使用Kotlin協(xié)程可以這樣寫:
首先需要改造一下ioCode1(),ioCode2(),ioCode3()異步函數(shù), 加上suspend
標(biāo)識(shí)為掛起函數(shù), suspend并不會(huì)幫我們切換線程,需要加上withContext(Dispatchers.IO)
來指定到IO線程
GlobalScope.launch(Dispatchers.Main) {
ioCode1()
uiCode1()
ioCode2()
uiCode2()
ioCode3()
uiCode3()
}
suspend fun ioCode1() {
withContext(Dispatchers.IO){
showLog("ioCode1 Thread name: ${Thread.currentThread().name}")
}
}
//同樣的寫法, 省略ioCode2() , ioCode3()
...
打印結(jié)果:
D/ouwen: ioCode1 Thread name: DefaultDispatcher-worker-1
D/ouwen: uiCode1 Thread name: main
D/ouwen: ioCode2 Thread name: DefaultDispatcher-worker-3
D/ouwen: uiCode2 Thread name: main
D/ouwen: ioCode3 Thread name: DefaultDispatcher-worker-2
D/ouwen: uiCode3 Thread name: main
Dispatchers.Main : 把線程環(huán)境指定為Main主線程,當(dāng)掛起函數(shù)ioCode1()執(zhí)行完成后,會(huì)自動(dòng)切換回到Main主線程環(huán)境
GlobalScope.launch(Dispatchers.Main) {
//運(yùn)行在 Main主線程環(huán)境
}
4.協(xié)程有什么優(yōu)勢(shì)?
a.性能的優(yōu)勢(shì)
這是相對(duì)而言的,例如:
Java: 當(dāng)有某個(gè)函數(shù)方法存在耗時(shí),但是調(diào)用的時(shí)候又不清楚這個(gè)函數(shù)耗時(shí),而放在主線程調(diào)用,就存在性能問題
Kotlin: 當(dāng)有某個(gè)函數(shù)方法存在耗時(shí),寫成suspend函數(shù),那么調(diào)用的時(shí)候,就必須開啟協(xié)程來調(diào)用,更好的避免性能問題
b.代碼簡(jiǎn)潔的優(yōu)勢(shì)
線性調(diào)用函數(shù),可以用看起來同步的代碼處理異步問題,不存在回調(diào)嵌套
線程可以自動(dòng)切回到原來的環(huán)境
5.suspend關(guān)鍵字是什么?
suspend關(guān)鍵作用: 標(biāo)識(shí)和提醒,提醒suspend函數(shù)必須在協(xié)程里面調(diào)用
如果一個(gè)函數(shù)有suspend關(guān)鍵字,那么該函數(shù)是掛起函數(shù),并且這個(gè)函數(shù)必須在另外一個(gè)掛起函數(shù)或者協(xié)程里面被調(diào)用,否則代碼就會(huì)提示報(bào)錯(cuò),無法通過編譯期
suspend不會(huì)切換線程 ,真正切換線程的操作是調(diào)用其他函數(shù)來實(shí)現(xiàn)
例如上面的: withContext(Dispatchers.IO)