Kotlin Coroutines実行非同期ロード例詳細


前言
Kotlin Coroutinesは、Kotlinから発売された新しい非同期APIである。すべての問題を解決するための最良の方法ではないが、多くの場合にはもっと簡単にできるようにしたい。ここではこの倉庫の具体的な使用方法を簡単に示します。以下は多く話しません。詳しく紹介してみましょう。
Coroutinesを導入する

// 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枚の画像をロードします。非同期のロードタスクは以下の通りです。

fun loadBitmapFromMediaStore(imageId: Int, 
        imagesBaseUri: Uri): Bitmap {
 val uri = Uri.withAppendedPath(imagesBaseUri, imageId.toString())
 return MediaStore.Images.Media.getBitmap(contentResolver, uri)
}
この方法はバックグラウンドスレッドで実行しなければならない。彼はIO操作に属しているので、バックグラウンドタスクを起動する多くの解決策があることを意味し、この方法が戻ったらすぐにImageviewに表示する必要がある。

imageView.setImageBitmap(bitmap)
この行のコードはメインスレッドで実行しなければなりません。
上記の3行のコードが一緒に書かれていると、プログラムが死んだり、フラッシュが切れたりします。これは合理的な選択スレッドによって決まります。次に、コツリントを使ったCoroutinesはどうやってこの問題を解決しますか?

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パラメータCoroutine Coottextはバックグラウンドスレッドで実行されることを保証しています。したがって、アプリケーションがカード死やフラッシュバックしないことを保証します。下に示すようにCoroutineCoroutineCotextを宣言してもいいです。

internal val Background = newFixedThreadPoolContext(2, "bg")
これは新しいコンテキストを作成し、彼のタスクを実行する際に通常のスレッドを2つ使用します。
次にlaunch(UI)について話します。これはもう一つのcorountineをトリガし、Androidで実行されます。
のメインスレッドです。
キャンセル可能
次の挑戦はActivity声明の周期に関するものを処理することです。タスクをロードする前に、実行が完了していない時にActivityを出発しました。彼はimageView.setImageBitmap(bitmap)を呼び出してcrashを起こすので、activityを離れる前にこのタスクをキャンセルしなければなりません。ここでlaunch()メソッドの戻り値jobを使いました。activity Stopメソッドを呼び出した。私たちはジョブをキャンセルするためにjobを使う必要があります。

job.cancel()
これはRxjavaを使う時にdisposeを呼び出して、AsyncTaskを使う時にcancel関数を呼び出すのと同じです。
LifecycleObserver
Android Architecture ComponentsはAndroid開発者に特に多くの強力なライブラリを提供しています。その中の一つはLifecycle APIです。リアルタイムの傍受activityとfragmentのライフサイクルを簡単に提供してくれました。コードをcorountiesと一緒に使います。

class CoroutineLifecycleListener(val deferred: Deferred<*>) : LifecycleObserver {
 @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
 fun cancelCoroutine() {
 if (!deferred.isCancelled) {
  deferred.cancel()
 }
 }
}
LifecycleOwnerの拡張関数を作成します。

fun <T> LifecycleOwner.load(loader: () -> T): Deferred<T> {
 val deferred = async(context = Background, 
      start = CoroutineStart.LAZY) {
 loader()
 }
 lifecycle.addObserver(CoroutineLifecycleListener(deferred))
 return deferred
}
この方法には新しいものが多すぎます。これから説明します。
今は1つのactivityまたはfragmentでload()を呼び出し、この関数からライフサイクルのメンバーにアクセスし、私たちのCoroutineLifecycleListenerを観察者に追加します。
load方法はパラメータとして、汎用タイプTを返します。load方法では、もう一つのCoroutineの作成者async関数を呼び出しました。Background cooutineコンテキストを使ってバックグラウンドスレッドでタスクを実行します。この方法にはもう一つのパラメータstart=CoroutineStart.LAZYがあります。呼び出されるまで知っています。
corountineは次いでDefered<T>のオブジェクトを使用者に返します。これは前のJobと似ていますが、従来のJava APIのJavaScript PromiseまたはFuture <T>のような遅延値を持ってもいいです。もっといいのは彼がawaitの方法を持っています。
次に、もう一つの拡張関数then()を定義します。今回はDeferen<T>の上で定義されています。私たちの上のロード方法が返すタイプです。また、一つのlamdaをパラメータとしてblockと命名して、Tタイプの単一のオブジェクトをそのパラメータとして使用します。

infix fun <T> Deferred<T>.then(block: (T) -> Unit): Job {
 return launch(context = UI) {
 block([email protected]())
 }
}
この関数はlaunch()関数を使って別のCoroutineを作成します。今回はメインスレッドで実行します。このCoroutineに渡されたlamberは、完了したDeferredオブジェクトの値をそのパラメータとする。私たちはawait()を呼び出して、Deferredオブジェクトが値を返すまで、このCoroutineの実行を保留します。
ここはcooutineがこんなに印象的になったところです。await()の呼び出しはメインスレッドで完了したが、このスレッドのさらなる実行をブロックすることはない。これは、関数の実行を簡単に停止し、それが準備されるまで、回復し、遅延値をlambankに渡すときに。corountineが一時停止した場合、メインスレッドは他のことを実行し続けることができます。await関数はcorountineの中の一つの核心概念で、何が全体の物事を創造してこのように魔力があります。load()関数に追加されたライフサイクル観測者は、onDestroy()を呼び出して最初のコロナティをキャンセルします。これはまた、block()が呼び出されるのを阻止するために、2番目のcooutineがキャンセルされます。
Kotlin Coroutine DSL
二つの拡張関数と一つのキャンセルされたクラスを得ました。どうやって使うかを見てみましょう。

load {
 loadBitmapFromMediaStore(imageId, imagesBaseUri)
} then {
 imageView.setImageBitmap(it)
}
上記のコードでは、Bitmapに戻るまでは、バックグラウンドスレッド上で機能を実行しなければならないというloadBitmap MediaStoreメソッドを呼び出し、load方法の戻り値はDeferred<Bitmap>である。
拡張関数として、then()方法はinfix声明を使用して、ロード方法ではDeferred<Bitmap>を返しますが、then方法に転送されるbitmap戻り値を返しますので、直接にthen方法でimageView.setImageBitmap(it) を呼び出すことができます。
上記のコードは、上記の例のように、バックグラウンドスレッド上で発生する非同期呼び出しを必要とする場合にも使用できます。これはRxJavaのように複数の呼び出しを作成することができますが、読みやすく、多くの最も一般的な状況をカバーすることができます。今はこのようなことを安全に行うことができます。各呼び出し中にcontextが漏れたり、スレッドが処理されたりすることを心配する必要はありません。

load { restApi.fetchData(query) } then { adapter.display(it) }
then()とロード()の方法はこの新しいライブラリの氷山の一角にすぎないですが、将来的にKotlinベースのAndroidライブラリに類似のものが出現したら、corountineバージョンが安定したバージョンに達することを期待しています。その前に、上のコードを使ったり、修正したり、Anko Coroutinesを確認したりすることができます。私はGitHubでもっと完全なバージョンを発表しました。https://github.com/ErikHellman/KotlinAsyncWithCoroutines ( ローカルダウンロード).
締め括りをつける
以上はこの文章の全部の内容です。本文の内容は皆さんの学習や仕事に対して一定の参考となる学習価値を持っています。質問があれば、メッセージを書いて交流してください。ありがとうございます。