現在の Java/Android の concurrency フレームワークはコールバック地獄の原因となります。
それは、スレッドセーフを保証するシンプルな方法がないからです。
kotlin coroutine は、concurrency を管理するための効果的でシンプルなフレームワークです。
Suspending と Blocking
coroutine は、スレッドを置き換えるものではなく、それを管理するフレームワークのようなものです。
元のスレッドをブロックすることなく、バックグラウンド処理完了に対しての wait を可能にする実行コンテキストを定義しています。
コールバックを避けて、簡単に concurrency を行ってみましょう。
基本
最初は非常にシンプルな例です。UIコンテキストで coroutine を起動し、その中でIOコンテキストでイメージを取得します。
その後、UIコンテキストに戻ります。
launch(UI) {
val image = withContext(IO) { getImage() }
imageView.setImageBitmap(image)
}
シングルスレッドのように直感的なコードです。
getImage() がIOスレッドで実行されている間、メインスレッドは、自由に他の処理を行うことができます。
withContext は、その coroutine を getImage() が実行されている間は中断します。
getImage() のあとすぐに、メインlooperが利用可能になり、coroutine はメインスレッドを再開します。
そして imageView.setImageBitmap(image) が呼ばれます。
次の例は、2つのバックグラウンド処理を完了させ、その結果を利用する、というものです。
async/await を使ってパラレルに実行し、それら両方の結果取得のあとメインスレッドでそれを利用した処理を行います。
val job = launch(UI) {
val deferred1 = async { getFirstValue() }
val deferred2 = async(IO) { getSecondValue() }
useValues(deferred1.await(), deferred2.await())
}
job.join()
async は launch に似ていますが、deferred (Kotlin版 Future) を返します。
そして、await() で結果を取得します。パラメータなしで実行した場合は、CommonPool コンテキストで実行されます。
前の例と同様に、2つの結果を待っている間は、メインスレッドは自由に処理を行うことができます。
例にあるように、launch は処理が完了するのを待つのに利用できる Job を返します。
「スレッドをブロックせず coroutine をブロックする」ということを除けば、他の言語と同様です。