今回は、Kotlin Coroutines について解説します。
なお、ここに掲載しているソースコードは以下の環境で動作確認しています。
- Android Studio Chipmunk | 2021.2.1 Patch 1
- JDK 11.0.12
- Android Gradle Plugin 7.2.1
- Kotlin 1.7.0
- Gradle 7.4.2
- org.jetbrains.kotlinx:kotlinx-coroutines-core 1.6.3
そして、アプリの build.gradle ファイルに kotlinx-coroutines-core
をインポートしています。
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3"
}
基本
Coroutines とは Kotlin にて非同期処理を実現する機能です。
例えば、Coroutines で非同期処理を実現する場合、以下のように実装します。
fun main() { // 新しい CoroutineScope を実行する。 runBlocking { // 新しい CoroutineScope を実行しつつ、 // 現在のスレッドをブロックしない。 launch { // ノンブロッキングで処理を遅延させる。 delay(100L) println("World!") } println("Hello,") } }
上記のコードを実行すると、以下の結果を得られます。
Hello, World!
実装を上から見ると、"World!"
を先に標準出力して、後に "Hello,"
を標準出力するように見えます。
しかし、launch
の後に続くブロック内の処理と println("Hello,")
は並列に実行されており、さらに launch
の後に続くブロックでは、最初に delay(100L)
で少し処理を中断しているため、実際には "Hello,"
から "World!"
の順番に標準出力されます。
詳細
ここでは、Coroutines によく登場する以下の 4 つについて解説します。
- CoroutineScope
- CoroutineContext
- Coroutine ビルダー
- suspend 関数
CoroutineScope
CoroutineScope とは Coroutine の有効範囲です。Coroutines で処理を実行したい場合、この CoroutineScope を開始する必要があります。
また、以下のように CoroutineScope は内部に CoroutineContext を保持しており、CoroutineContext の設定通りに Coroutine を実行します。
public interface CoroutineScope { public val coroutineContext: CoroutineContext }
CoroutineContext
CoroutineContext とは Coroutine の設定のようなものです。CoroutineContext を保持する CoroutineScope は、CoroutineContext の設定通りに Coroutine を実行します。
以下に、代表的な CoroutineContext の要素を記載します。
要素 | 説明 |
---|---|
Job | Coroutine のハンドラ。Coroutine のキャンセルができる。 |
CoroutineDispatcher | Coroutine が動作するスレッドを指定する。 |
CoroutineName | Coroutine の名前。デバッグでこの名前が表示される。 |
CoroutineExceptionHandler | Exception のハンドラ。Coroutine でキャッチできなかった Exception を処理できる。 |
Coroutine ビルダー
Coroutine ビルダーとは CoroutineScope を開始する関数です。さきほどのサンプルコードでは、runBlocking
と launch
が Coroutine ビルダーです。Coroutine ビルダーに Coroutines で実行したい処理を渡すと、その処理は CoroutineScope 内部で実行されます。
以下に、代表的な Coroutine ビルダーを記載します。
Coroutine ビルダー | 特徴 |
---|---|
runBlocking | 現在のスレッドをブロックする。 CoroutineScope の外からでも呼び出せる。 ブロック内部の処理の結果を返せる。 |
launch | 現在のスレッドをブロックしない。 CoroutineScope の内部からのみ呼び出せる。 ブロック内部の処理の結果を返せない。 |
async | 現在のスレッドをブロックしない。 CoroutineScope の内部からのみ呼び出せる。 ブロック内部の処理の結果を返せる。 |
上記の 3 つの内、launch と async については、CoroutineScope の拡張関数として定義されているため、CoroutineScope の内部からしか呼び出すことができません。runBlocking については、CoroutineScope の拡張関数ではないため、CoroutineScope の外からでも呼び出すことができます。
suspend 関数
suspend 関数とは、スレッドをブロックせずに処理を一時停止し、そして再開できる関数のことです。さきほどのサンプルコードでは、delay
が suspend 関数です。delay は処理を一時停止する関数ですが、現在のスレッドをブロックせずにスレッドプールに一時返却するため、delay で一時停止している間、別の処理がそのスレッドを使用できます。
また、suspend 関数は suspend 関数からしか呼ぶことができません。よって、例えば delay を呼び出すためには、その呼び出し元の関数に suspend を付与する必要があります。
// NG fun doWork() { delay(100L) println("Do work.") }
// OK suspend fun doWork() { delay(100L) println("Do work.") }