1、先看一下suspend單獨(dú)使用時(shí)候的線程切換情況,以便于更清晰的理解suspend:
以上代碼可以看出來洼哎,一切正常,也可以很好的理解suspend掛起到底是怎么進(jìn)行線程切換的沼本,但是如果這里的代碼放到ViewModel里卻出現(xiàn)了線程無法切換的情況
class NotificationsViewModel : ViewModel() {
Log.e("dz", if(Looper.getMainLooper() == Looper.myLooper()) "主線程pre" else "子線程pre")
GlobalScope.launch {
? ? ? ? ? ? withContext(Dispatchers.IO){
? ? ? ? ? ? ? ? Log.e("dz",if(Looper.getMainLooper() ==Looper.myLooper())"主線程suspend-start" else "子線程suspend-start")
????????????????Thread.sleep(3000L);
????????????????????Log.e("dz",if(Looper.getMainLooper() ==Looper.myLooper())"主線程suspend-end" else "子線程suspend-end")
? ? ? ? ? ? }
? ? ? ? ? ? Log.e("dz",if (Looper.myLooper() ==Looper.getMainLooper())"主線程" else "子線程");
以上代碼輸出:
2021-03-16 15:57:26.377 15976-15976/com.dq.qkotlin E/dz: 主線程pre
2021-03-16 15:57:26.416 15976-16523/com.dq.qkotlin E/dz: 子線程suspend-start
2021-03-16 15:57:29.417 15976-16523/com.dq.qkotlin E/dz: 子線程suspend-end
2021-03-16 15:57:29.419 15976-16523/com.dq.qkotlin E/dz: 子線程
最大的問題就在于最后一行 居然沒想Activity一樣正池停回到主線程,但是如果把GlobalScope修改為viewModelScope抽兆,那么一切正常识补,最后一句打印”主線程“
說明了ViewModel里一定不能用GlobalScope,而是要用viewModelScope
2辫红、采用以上suspend+retrofit方式請(qǐng)求網(wǎng)絡(luò)凭涂,如果網(wǎng)絡(luò)錯(cuò)誤,會(huì)直接崩潰!
如果什么都不處理贴妻,ViewModel.kt的第39行代碼會(huì)直接崩潰切油,壓根就無法打印41行的Log
3、delay 和 sleep的區(qū)別
下面的代碼名惩,連續(xù)點(diǎn)擊不會(huì)卡死主線程澎胡,無論是用delay還是sleep都不會(huì)卡死,但是選擇delay還是sleep會(huì)影響用哪個(gè)子線程
這也是最常用的協(xié)程方式
```java
requireView().findViewById<TextView>(R.id.mainScope).setOnClickListener{v->
???// 第一步 進(jìn)入這里,main是主線程
???mainScope.launch(){
???????println("mainScope start:${Thread.currentThread().name}")
???????// 第三步 進(jìn)入這里攻谁,main是主線程稚伍。也就是說:這里已經(jīng)有post(runable)的感覺了
???????withContext(Dispatchers.IO){
???????????println("mainScope -> withContext coroutine1:${Thread.currentThread().name}")
???????????// 第四步 進(jìn)入這里,DefaultDispatcher-worker-2是子線程巢株,這里可能是worker-2槐瑞,也可能是worker-3熙涤,說明是線程池
???????????delay(10000)
???????????println("mainScope -> withContext coroutine2:${Thread.currentThread().name}")
???????????// 第五步阁苞,10秒后 進(jìn)入這里,DefaultDispatcher-worker-2是子線程祠挫,這里要么是worker-3那槽,要么是wworker-2。所以這里可能和上面第四步并不是一條線程等舔。
?? ??? ?? ? //注意1:即便你在上面10秒內(nèi)調(diào)用了?mainScope.cancel()骚灸,這里的第五步的代碼還是會(huì)走,只是下面第六步的代碼不走了
?? ??? ?? ? //注意2:如果上面的?delay(10000)?換成了?sleep(10000) ,那么第四步和第五步就是同一條線程
???????}
???????println("mainScope ends:${Thread.currentThread().name}")
???????// 第六步慌植,10秒后 進(jìn)入這里甚牲,main是主線程
???}
???println("mainScope outside end:${Thread.currentThread().name}")
???// 第二步 進(jìn)入這里,main是主線程
}
```
把上面的?Dispatchers.IO 改成Dispatchers.Main蝶柿,代碼執(zhí)行流程是一樣的丈钙,但是所有都是在主線程里。
神奇的是交汤,雖然代碼都在主線程里雏赦,但是只要用delay做延時(shí),就不會(huì)卡死主線程芙扎。
delay也是協(xié)程里的方法
```java
requireView().findViewById<TextView>(R.id.mainScope).setOnClickListener{v->
???// 第一步 進(jìn)入這里星岗,main是主線程
???mainScope.launch(){
???????println("mainScope start:${Thread.currentThread().name}")
???????// 第三步 進(jìn)入這里,main是主線程戒洼。也就是說:這里已經(jīng)有post(runable)的感覺了
???????withContext(Dispatchers.Main){
???????????println("mainScope -> withContext coroutine1:${Thread.currentThread().name}")
???????????// 第四步 進(jìn)入這里俏橘,main是主線程
???????????delay(10000)//如果這里改成sleep(10000),那么會(huì)卡死主線程
???????????println("mainScope -> withContext coroutine2:${Thread.currentThread().name}")
???????????// 第五步圈浇,10秒后 進(jìn)入這里敷矫,main是主線程
???????}
???????println("mainScope ends:${Thread.currentThread().name}")
???????// 第六步,10秒后 進(jìn)入這里汉额,main是主線程
???}
???println("mainScope outside end:${Thread.currentThread().name}")
???// 第二步 進(jìn)入這里曹仗,main是主線程
}
```