引入攜程所需庫
第一步:項目級build.gradle中
buildscript { ext.kotlin_coroutines = '1.3.1' }
第二步:Module級別build.gradle中
dependencies { // ?? 依賴協程核心庫 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines" // ?? 依賴當前平臺所對應的平臺庫,如Android或js implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines" //協程的聲明周期庫,該庫可以選加,是讓協程的生命周期和Activity的周期保持一致 implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha02' }
注:核心庫和平臺庫版本需要保持一致
協程是什么
- 協程:是一個線程框架,最核心的部分是【非阻塞式】和【掛起】;就是launch{協程部分}async{協程部分}
相關名詞
1、GloabScope:全局范圍
2、Coroutine:協程
3、suspend:掛起、暫停
- a、代碼執行到用suspend標識的函數時會掛起,且這種掛起是不會阻塞的,不會影響當前線程的執行,而這種所謂的掛起,其實就是切一個線程,且在函數執行完畢會自動將線程切回來的調度工作。
- b、用suspend標識的函數只能運行在協程中;或者被另一個被suspend標識的函數調用,其實最終還是運行在一個協程中。
- c、suspend本身是不能真正實現掛起的,他的作用主要是一個提醒,是創建者對使用者的提醒,該函數是一個耗時函數,且是用掛起的方式在后臺運行,所以請在協程中調用我
- d、suspend的意義:傳遞CoroutineContext
相關方法介紹
runBlocking :一般用于 單元測試中,是線程阻塞的
launch(Dispatchers.IO){}:可以用來切線程
coroutineScope.launch(Dispatchers.IO) {
...
launch(Dispatchers.Main){
...
launch(Dispatchers.IO) {
...
launch(Dispatchers.Main) {
...
}
}
}
}
withContext(){}:切換線程,當需要頻繁的進行線程切換時,因為可以自動切回原來的線程,所以相應的代碼嵌套層次沒那么多,并在閉包內的邏輯執行結束后自動切回原來的線程,繼續執行
coroutineScope.launch(Dispatchers.Main) {
...
withContext(Dispatchers.IO) {
...
}
...
withContext(Dispatchers.IO) {
...
}
...
}
Async{}:返回的協程實現了Deferred??梢允褂脀ait()方法
創建攜程的方法
方法一:使用runBlocking 頂層函數,適用于單元測試的情況,是線程阻塞的,一般正常的業務開發不會使用
runBlocking{個人理解:此次相當于開啟一個線程,但是會阻塞當前線程,相當于一個耗時任務??? getImage(imageId) }
方法二:使用GlobalScope 單例對象,直接調用launch開啟協程,不會阻塞線程,但是實際開發中也一般不會使用,因為它的生命周期和APP的生命周期是一致的
GlobalScope.launch{ getImage(imageId) }
方法三:通過CoroutineContext創建一個CoroutineScope(接口)對象,需要一個類型為CoroutineContext的參數。開發中推薦這種方法,--coroutineContext?????
val coroutineScope = CoroutineScope(coroutineContext) coroutineScope.launch{ getImage(imageId) }
方法四:主要用于在一個協程中開啟另一個協程,直接在普通代碼中無法使用
launch{ }
方法五:該方法與launch類似,也是在協程中使用
async{ }
async 和 launch對比
相同點:都可以用來啟動一個協程,返回的都是Coroutine
不同點:
async返回的Coroutine還實現了Deferred(意思是延遲,也就是結果稍后才能拿到。)接口。可以使用await暫停函數來返回result
launch可啟動新協程而不將結果返回給調用方
async使用場景:如現在需要展示公司LOGO和個人頭像,但是需要等待兩者都返回了,才可以去顯示,兩者同時進行,然后等較慢的執行完畢就可以顯示了,所花費的時間就是兩個中較長的一個,而不是兩者之和
coroutineScope.launch(Dispatchers.Main) {
// ?? async 函數啟動新的協程
val avatar: Deferred = async { api.getAvatar(user) } // 獲取用戶頭像
val logo: Deferred = async { api.getCompanyLogo(user) } // 獲取用戶所在公司的 logo
// ?? ?? 獲取返回值
show(avatar.await(), logo.await()) // 更新 UI
}
掛起的本質
什么是掛起:掛起就是稍后會自動切回來的操作
在協程中,我們掛起的對象既不是線程,也不是函數,而是我們掛起的對象是協程,就是launch和async函數中的閉包的代碼塊,而launch和async或者其他函數創建的協程在執行到某一個suspend函數的時候,這個函數就會被掛起,或者說從當前線程中脫離。
自定義suspend函數
-
什么時候需要自定義suspend函數
比如某個函數時比較函數的,可以將他協程suspend函數,這就是原則 耗時操作分為:I/O操作和CPU計算工作,比如文件讀寫,網絡交互,圖片的模糊處理。 另一種耗時操作:如事情本身并不慢,但是因為某些原因需要等5s或者特定時間再執行,這種也可以定義為suspend
-
具體如何實現自定義
1、給函數加上suspend關鍵字 2、用withContext將函數內容包?。ǚ奖銏绦型戤叄詣忧谢卦季€程) 3、針對需要等待的耗時操作,我們可以直接使用另一個掛起函數delay()