一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - JAVA教程 - Kotlin Coroutines執行異步加載示例詳解

Kotlin Coroutines執行異步加載示例詳解

2021-03-29 09:48尺錘 JAVA教程

這篇文章主要給大家介紹了關于Kotlin Coroutines執行異步加載的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。

前言

Kotlin Coroutines是Kotlin推出的新的異步API。并不是解決所有問題的最優方案,但是希望在許多情況下它會使事情變得更容易一些。這里只簡單的展示一下這個庫在安卓中的具體使用方案。下面話不多說了,來一起看看詳細的介紹吧。

引入Coroutines

?
1
2
3
4
5
6
7
8
9
10
//在application的build.gradle文件中的android節點添加如下的代碼
kotlin {
 experimental {
  coroutines 'enable'
 }
}
 
//添加下面兩行到依賴中
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.20"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.20"

第一個Coroutines示例

通常我們加載一張圖片到ImageView中,異步的加載任務如下所示:

?
1
2
3
4
5
fun loadBitmapFromMediaStore(imageId: Int,
        imagesBaseUri: Uri): Bitmap {
 val uri = Uri.withAppendedPath(imagesBaseUri, imageId.toString())
 return MediaStore.Images.Media.getBitmap(contentResolver, uri)
}

這個方法必須在后臺線程中執行,因為他屬于一個IO操作,這意味著我們有很多解決方案可以啟動后臺任務,一旦該方法返回一個bitmap,我們需要立即顯示在Imageview中。

?
1
imageView.setImageBitmap(bitmap)

這行代碼必須在主線程執行,否則會crash。

以上三行代碼如果寫到一起將會導致程序卡死或者是閃退,這都取決于合理的選擇線程。接下來我們看一下使用kotlin的Coroutines是如何解決這個問題的:

?
1
2
3
4
5
6
7
val job = launch(Background) {
 val uri = Uri.withAppendedPath(imagesBaseUri, imageId.toString())
 val bitmap = MediaStore.Images.Media.getBitmap(contentResolver,
 launch(UI) {
 imageView.setImageBitmap(bitmap)
 }
}

這里最重要的是launch()和參數Background和UI,launch()表示創建和啟動一個Coroutine,Background參數CoroutineContext用來保證在后臺線程執行,從而保證應用程序不會卡死或者閃退,你可以聲明一個如下面所示的CoroutineContext。

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

這將創建一個新的上下文,并在執行他的任務的時候使用兩個常規的線程。

接下來說launch(UI),這將觸發另一個coroutine,將執行在Android
的主線程。

可取消

接下來的挑戰是處理跟Activity聲明周期相關的東西,當你在加載一個任務,還沒有執行完的時候離開了該Activity,以至于他在調用imageView.setImageBitmap(bitmap)就會引起crash,所以我們在離開該activity之前就需要取消該任務,這里就用到了launch()方法的返回值job,當activity調用onStop方法時,我們需要使用job來取消任務

?
1
job.cancel()

這就像你使用Rxjava時調用dispose和使用AsyncTask時調用cancel函數是一個道理。

LifecycleObserver

Android Architecture Components 給安卓開發者提供了特別多強大的庫,其中之一就是Lifecycle API.給我們提供了一個簡便的方法用來實時的監聽activity和fragment的生命周期,我們定義一下代碼與coroutines一起使用。

?
1
2
3
4
5
6
7
8
class CoroutineLifecycleListener(val deferred: Deferred<*>) : LifecycleObserver {
 @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
 fun cancelCoroutine() {
 if (!deferred.isCancelled) {
  deferred.cancel()
 }
 }
}

我們創建一個LifecycleOwner的擴展函數:

?
1
2
3
4
5
6
7
8
fun <T> LifecycleOwner.load(loader: () -> T): Deferred<T> {
 val deferred = async(context = Background,
      start = CoroutineStart.LAZY) {
 loader()
 }
 lifecycle.addObserver(CoroutineLifecycleListener(deferred))
 return deferred
}

這個方法中有太多新的東西,接下來一一解釋:

現在我們可以在一個activity或fragment中調用load() ,并從該函數中訪問生命周期成員,并將我們的CoroutineLifecycleListener添加為觀察者。

load方法需要一個loader作為參數,返回一個通用類型T,在load方法中我,我們調用了另外一個Coroutine的創造者async()函數,將會使用Background coroutine上下文在后臺線程中執行任務,注意這個方法還有另外一個參數start = CoroutineStart.LAZY,這意味著coroutine不會立即執行,知道被調用為止。

coroutine接著會返回一個Defered<T>對象給調用者,這與我們之前的Job類似,但它也可以攜帶一個延遲值,如常規Java API中的JavaScript Promise或Future <T> ,更好的是他有一個await方法.

接下來我們定義另外一個擴展函數then() ,這次是在Deferen<T>上面定義,是我們上面的load方法返回的類型,它還將一個lambda作為參數,命名為block,它將T類型的單個對象作為其參數。

?
1
2
3
4
5
infix fun <T> Deferred<T>.then(block: (T) -> Unit): Job {
 return launch(context = UI) {
 block(this@then.await())
 }
}

這個函數將使用launch()函數創建另一個Coroutine ,這次在主線程上運行。傳遞給此Coroutine的lambda(命名塊)將完成的Deferred對象的值作為其參數。我們調用await()來掛起這個Coroutine的執行,直到Deferred對象返回一個值。

這里是coroutine變得如此令人印象深刻的地方。 await()的調用是在主線程上完成的,但是不會阻塞該線程的進一步執行。它將簡單地暫停該函數的執行,直到它準備好,當它恢復并將延遲值傳遞給lambda時。coroutine暫停時,主線程可以繼續執行其他的事情。await函數是coroutine中的一個核心概念,是什么創造了整個事物如此有魔力。

load()函數中添加的生命周期觀察者將在我們的activity上調用onDestroy()后取消第一個coroutine。這也會導致第二個coroutine被取消,阻止block()被調用。

Kotlin Coroutine DSL

現在我們得到了兩個擴展函數和一個會處理coroutine被取消的類,讓我們來看看如何使用:

?
1
2
3
4
5
load {
 loadBitmapFromMediaStore(imageId, imagesBaseUri)
} then {
 imageView.setImageBitmap(it)
}

上面的代碼中,我們將lambda方法傳給load函數,該函數調用loadBitmapFromMediaStore方法,該函數必須在后臺線程上執行,直到該方法返回一個Bitmap,load方法的返回值是Deferred<Bitmap>

作為擴展函數,then()方法使用infix聲明,盡管load方法中返回的是Deferred<Bitmap> ,但是將會傳送給then方法一個bitmap返回值,所以我們可以直接在then方法中調用imageView.setImageBitmap(it)

上面的代碼可以用于任何需要在后臺線程上發生的異步調用,以及返回值應該返回到主線程的地方,就像上面的例子。它不像RxJava那樣可以編寫多個調用,但它更容易閱讀,可能會涵蓋很多最常見的情況。現在你可以安全地做這樣的事情,而不必擔心在每個調用中造成context泄漏或處理線程;

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

then()和load()方法只不過是這個新庫的冰山一角,但是我確實希望在未來的基于Kotlin的Android庫中出現類似的東西,一旦coroutine版本達到穩定版本。在此之前,您可以使用或修改上面的代碼,或者查看Anko Coroutines。我還在GitHub上發布了一個更完整的版本。 (https://github.com/ErikHellman/KotlinAsyncWithCoroutines ).

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:https://www.jianshu.com/p/6cbc97258952

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 人体做爰aaaa免费 | 性色xxx | 夫妻性生活影院 | 护士的小嫩嫩好紧好舒服 | 亚洲视频在线一区二区 | 国产精品边做边接电话在线观看 | 久9青青cao精品视频在线 | 狠狠色伊人亚洲综合网站色 | 国产灌醉| 青草久久精品亚洲综合专区 | 99资源在线观看 | 情欲综合网 | 毛毛片在线 | 国产裸露片段精华合集链接 | 精品免费久久久久久影院 | 第四色男人天堂 | 欧美性理论片在线观看片免费 | 天天色资料 | 青青青青青国产费线在线观看 | 操爽| 国产日产欧产精品精品软件 | 奇米影视一区 | 亚瑟天堂久久一区二区影院 | 初尝黑人巨大h文 | 日本高清中文字幕一区二区三区 | 日韩在线 中文字幕 | 国产成人久久 | 91网红福利精品区一区二 | 亚洲午夜久久久 | 亚洲AVAV天堂AV在线网爱情 | 免费一级毛片完整版在线看 | 欧美一级v片| 亚洲精品久久久打桩机 | 亚洲黄网站wwwwww | 日韩理论片在线看免费观看 | 国产第一综合另类色区奇米 | 亚洲精品国产在线网站 | 美女用屁股把人吞进肚子 | 精品国产自在在线在线观看 | 九九99热久久999精品 | 青春学堂在线观看 |