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