真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

如何在Kotlin中使用協(xié)程實現(xiàn)一個異步加載功能-創(chuàng)新互聯(lián)

如何在Kotlin中使用協(xié)程實現(xiàn)一個異步加載功能?相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

黃山ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)公司的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!

使用Coroutine之前的初始配置

首先我們使用android studio 新建一個項目,并在新建項目的時候勾選【Include Kotlin support】,就像下邊這樣

如何在Kotlin中使用協(xié)程實現(xiàn)一個異步加載功能

項目創(chuàng)建成功后,我們需要在build.gradle文件中的android配置模塊下面增加如下的配置

kotlin {
 experimental {
 coroutines 'enable'
 }
}

然后在build.gradle文件中添加如下的依賴

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.20'
 implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.20'

完整的配置情況如下:

如何在Kotlin中使用協(xié)程實現(xiàn)一個異步加載功能

經(jīng)過上邊的步驟Coroutine的配置就已經(jīng)完成了。接下來我們就可以使用Coroutine了。

實現(xiàn)你的第一個Coroutine程序

現(xiàn)在我們來開始編寫我們的第一個Coroutine例子程序,這個程序的主要功能就是從手機媒體中加載一張圖片,并把它顯示在一個ImageView中。我們先來看看在未使用Coroutine之前使用同步的方式加載圖片的代碼如下:

val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
imageView.setImageBitmap(bitmap)

在上邊的代碼中我們從媒體讀取了一張圖片并把它轉(zhuǎn)化成Bitmap對象。因為這是一個IO操作,如果我們在UI主線程中調(diào)用這段代碼,將可能導(dǎo)致程序卡頓或產(chǎn)生ANR崩潰,所以我們需要在新開的線程中調(diào)用下邊的代碼

val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)

接著我們需要在UI線程中調(diào)用下邊的代碼來顯示加載的圖片

imageView.setImageBitmap(bitmap)

為了實現(xiàn)這一功能在傳統(tǒng)的android程序中我們需要使用Handler或AsyncTask將結(jié)果從非UI主線程發(fā)送到UI主線程進行顯示,我們需要編寫許多額外的代碼。并且這些代碼的可讀性也不是十分的友好。下邊我們來看看使用Kotlin的Coroutine來實現(xiàn)圖片的加載的代碼,如下:

val job = launch(Background) {
 val bitmap = MediaStore.Images.Media.getBitmap(contentResolver,uri) 
 launch(UI) {
 imageView.setImageBitmap(bitmap)
 }
}

我們先忽略返回值job,我們稍后會進行介紹,在這兒我們關(guān)心的事情是launch函數(shù)和參數(shù)Background與UI。與之前使用同步的方式加載圖片相比唯一的不同就在于這兒我們調(diào)用了lauch函數(shù)。lauch()創(chuàng)建并啟動了一個協(xié)程,這兒的參數(shù)Background是一個CoroutineContext對象,確保這個協(xié)程運行在一個后臺線程,確保你的應(yīng)用程序不會因耗時操作而阻塞和崩潰。你可以像下邊這樣定義一個CoroutineContext:

internal val Background = newFixedThreadPoolContext(2, "bg")

他將使用含有兩個線程的線程池來執(zhí)行協(xié)程里邊的操作。在第一個協(xié)程里邊我們又調(diào)用了launch(UI)創(chuàng)建并啟動了一個新的協(xié)程,這兒的UI并不是我們自己創(chuàng)建的,他是Kotlin在Android平臺里邊預(yù)定義的一個CoroutineContext,代表著在UI主線程中執(zhí)行協(xié)程里邊的操作。所以我們將更新程序界面的操作imageView.setImageBitmap(bitmap)放在了這個協(xié)程里。通過這兒的例子代碼你會發(fā)現(xiàn)在kotlin里邊使用協(xié)程來實現(xiàn)線程間的通信和切換非常的簡單,比RxJava還簡單??瓷先ゾ透銓懲降姆绞降拇a一樣。

取消協(xié)程

在上邊的例子中我們返回了一個Job類型的對象job。通過調(diào)用job.cancel()我們能夠取消一個協(xié)程。例如當我們退出當前Activity的時候,圖片還沒有加載完。這個時候我們就可以在onDestroy中調(diào)用job.cancel()來取消這個未完成的任務(wù)。這與我們使用Rxjava時調(diào)用dipose()或使用AsyncTask時調(diào)用cancel() 來取消未完成的操作的作用是一樣的。

LifecycleObserver

android 架構(gòu)組件( Android Architecture Components )里邊引入了許多非常好的東西,比如:ViewModel, Room 和 LiveData以及Lifecycle API。給予我們一種非常安全簡便的方式監(jiān)聽Activity和Fragment的生命周期變化。接下來我們將使用他們來對之前加載圖片的例子進行改進,利用lifecycle對Activity生命周期進行監(jiān)聽并做出相應(yīng)的處理(監(jiān)聽到Activity調(diào)用onDestroy()時自動取消后臺任務(wù))。

我們定義如下的代碼來使用協(xié)程:

class CoroutineLifecycleListener(val deferred: Deferred<*>) : LifecycleObserver {
 @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
 fun cancelCoroutine() {
 if (!deferred.isCancelled) {
 deferred.cancel()
 }
 }
}

我們也創(chuàng)建了LifecycleOwner的一個擴展函數(shù):

fun  LifecycleOwner.load(loader: () -> T): Deferred {
 val deferred = async(context = Background, start = CoroutineStart.LAZY) {
 loader()
 }

 lifecycle.addObserver(CoroutineLifecycleListener(deferred))
 return deferred
}

在這個函數(shù)里邊有許多新的東西,即使看上去感到疑惑也不要緊,我們會一步一步的對其進行講解。我們在所有實現(xiàn)LifecycleOwner接口的類中擴展了一個load函數(shù)。也就是說當我們使用支持庫的時候我們可以在Activity或Fragment中直接調(diào)用這個load函數(shù)(支持庫里邊的AppCompatActivity和Fragment實現(xiàn)了LifecycleOwner接口)。為了能夠在這個函數(shù)里邊訪問lifecycle成員添加CoroutineLifecycleListener作為一個觀察者。

load()函數(shù)使用名為loader的lambda表達式作為參數(shù)(這個lambda表達式返回一個泛型類型T),在load()函數(shù)里邊我們調(diào)用了名叫async的函數(shù),這個函數(shù)的作用也是用于創(chuàng)建一個協(xié)程。它使用Background作為上下文。注意第二個參數(shù)start = CoroutineStart.LAZY。它的意思是不會立即啟動一個協(xié)程。直到你顯示的請求他返回一個值的時候它才會啟動,稍后你會看到具體怎樣做。這個協(xié)程返回了一個Deferred對象到調(diào)用者。它與我們之前提到的job對象是類似的,但是他可以攜帶一個延遲的值,類似于JavaScript 中的Promise或Java APIs中的Future 。

接下來我們定義Deferred類(前面我們在load函數(shù)中返回的類型)的一個擴展函數(shù)then() ,它也使用一個名叫block的lambda表達式作為參數(shù)。這個lambda表達式以T類型的對象作為參數(shù)。具體代碼如下:

infix fun  Deferred.then(block: (T) -> Unit): Job {
 return launch(context = UI) {
 block(this@then.await())
 }
}

這個函數(shù)使用launch()創(chuàng)建了另外一個協(xié)程,這個新的協(xié)程將運行在程序的主線程中。我們在這個新的協(xié)程中調(diào)用了then函數(shù)中傳入的名叫block的lambda表達式并使用await()函數(shù)作為它的參數(shù)。await()是在主線程中調(diào)用的,但是他并不會阻塞主線程的執(zhí)行,它將掛起這個函數(shù),主線程可以繼續(xù)做其他的事情。當值從其他協(xié)程中返回的時候,他將被喚醒并將值從Deferred傳遞到這個lambda中。掛起函數(shù)(Suspending functions)是協(xié)程中最主要的概念。

一旦Activity的onDestroy方法被調(diào)用的時候,我們在load()函數(shù)中添加的lifecycle觀察者將會取消第一個協(xié)程,也會使第二個協(xié)程被取消,避免block()被調(diào)用。

Kotlin Coroutine DSL

上邊我們定義了兩個擴展函數(shù)和一個用于取消協(xié)程的類,讓我們來看看如何使用它們,代碼如下:

load {
 MediaStore.Images.Media.getBitmap(contentResolver,uri)
} then {
 imageView.setImageBitmap(it)
}

在上邊的代碼中我們傳遞一個lambda到load()函數(shù)中,在這個lambda中調(diào)用了loadBitmapFromMediaStore()函數(shù)運行在一個后臺進程中。一旦loadBitmapFromMediaStore()函數(shù)返回Bitmap,load()函數(shù)將返回Deferred 。擴展的函數(shù)then()是被infix修飾的,因此當Deferred返回之后我們可以使用上面那種奇特的語法調(diào)用它。我們傳遞到then()中的lambda將接收到一個Bitmap對象。因此我們可以簡單的調(diào)用imageView.setImageBitmap(it)顯示這個Bitmap。

上邊的代碼可以被應(yīng)用到任何別的需要使用異步調(diào)用并將值轉(zhuǎn)遞到主線程的操作中。和RxJava這種框架比起來Kotlin的協(xié)程可能沒有它那么強大。但是Kotlin的協(xié)程可讀性更強,也更簡單。現(xiàn)在你可以安全的使用它來執(zhí)行你的異步操作了,再也不用擔(dān)心內(nèi)存泄漏的發(fā)生了。如下是將上邊的代碼用于從網(wǎng)絡(luò)加載數(shù)據(jù)并顯示的例子:

load { restApi.fetchData(query) } then { adapter.display(it) }

看完上述內(nèi)容,你們掌握如何在Kotlin中使用協(xié)程實現(xiàn)一個異步加載功能的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!


分享標題:如何在Kotlin中使用協(xié)程實現(xiàn)一個異步加載功能-創(chuàng)新互聯(lián)
標題路徑:http://weahome.cn/article/ddosse.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部