コルーチンを利用した非同期処理
参考: AndroidでのKotlinコルーチン
参考: 図で理解するKotlinコルーチン
コルーチン
非同期処理が含まれるブロック{...}(=コルーチンスコープ)内で、非同期処理の実行中に、同期処理を中断できる仕組み(=デザインパターン)。
UIスレッドの処理によって呼び出された、ワーカースレッドでの処理を含む非同期処理のまとまり。
コルーチンを起動する、UIスレッドで動作する処理を処理A、
処理Aによって呼び出されたワーカースレッドで動作する処理を処理B、
処理Bによって呼び出されたUIスレッドで動作する処理を処理Cとすると、
処理Cは処理Bの終了を待つ必要があるため、同一処理ブロック{}でまとめて管理するのが望ましく、この処理のまとまりをコルーチンと呼ぶ。
また、処理Bと処理Cは互いに異なるスレッド上で動作するため、双方から見て非同期処理と言える。
コルーチンスコープ
コルーチンの生存可能環境。
コルーチンを管理しており、コルーチンの起動や実行中止を行う。
ViewModelでコルーチンを定義した場合はViewModelクラスのviewModelScopeプロパティ、
アクティビティでコルーチンを定義した場合はActivityクラスのlifecycleScopeプロパティがコルーチンスコープにあたる。
コルーチンを利用した非同期処理の実装
コルーチンを利用した非同期処理を実装する手順は、以下の通り。
Gradle Scripts/build.gradle(Module)のdependenciesブロックに、コルーチンの実装に必要な追加ライブラリを記述コルーチンの定義コルーチンスコープから2.で定義したコルーチンを起動
build.gradle(Module)への追加ライブラリの記述
コルーチンの実装に必要な機能は標準SDKで定義されていないため、追加ライブラリをGradle Scriptsフォルダのbuild.gradle(Module)ファイルに記述する必要がある。
また、ライブラリのバージョンは以下サイトを参考に、安定版の最新バージョン番号に書き換える。
Kotlinコルーチン
AndroidX Lifecycle
サンプルコード
...
dependencies {
// Kotlinコルーチン
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0"
// AndroidX Lifecycle
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
...
}
コルーチンの定義
参考: CoroutineScope
非同期処理を行う処理を記述する際、
ワーカースレッドでの動作を保証する@WorkerThreadアノテーションの記述に加え、
呼び出し元スレッドでの処理を中断させながら、指定スレッドで処理を実行できるwithContext()メソッドを利用する。
なお、withContext()メソッドは内部的にsuspendキーワードが付与されているため、withContext()メソッドを利用する親メソッドにもsuspendキーワードを付与する必要がある。
定義
// スレッドを分離して処理を実行
// <- 内部的にsuspendキーワードが付与
suspend fun <T> withContext(
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T
// パラメータ
// context: 分離先スレッド(Dispatchersクラス定数)
// block: スレッドを分離して実行する処理
Dispatchersクラス定数
| クラス定数 | 内容 |
|---|---|
Dispatchers.Main |
UIスレッド用途: UI操作 |
Dispatchers.IO |
ワーカースレッド(I/Oスレッド)用途: Roomコンポーネントファイルの 読み書きネットワークオペレーション
|
Dispatchers.Default |
ワーカースレッド(Defaultスレッド)用途: CPU負荷の高い作業 |
サンプルコード
// 非同期(=ワーカースレッド)で実行する処理
// -> ワーカースレッドで動作することを明示的に記述(@WorkerThreadアノテーション)
// suspend: 実行中は元スレッド(=UIスレッド)の他の処理を中断させる
@WorkerThread
private suspend fun backgroundTaskRunner(): String {
// ワーカースレッドで取得した値
val returnVal = withContext(Dispatchers.IO) {
... // ワーカースレッドで値を取得
}
// 取得した値を返却
return returnVal
}
suspendシグネチャ
suspendシグネチャが付与されたメソッドの実行中に、メソッドの呼び出し元スレッドでの処理を中断させるキーワード。
コルーチンスコープによるコルーチンの起動
アクティビティでコルーチンを定義した場合、アクティビティがもつlifecycleScopeプロパティを利用してコルーチンを起動する。
ViewModelでコルーチンを定義した場合、ViewModelクラスのviewModelScopeプロパティを利用してコルーチンを起動する。
定義
CoroutineScope.launch(
context: CoroutineContext
start: CoroutineStart
block: suspend CoroutineScope.() -> Unit
)
// パラメータ
// context: コルーチンスコープへの追加コンテキスト
// -> 未指定の場合はDispatchers.Default(コルーチンスコープから継承)
// start: コルーチンの開始タイミング(CoroutineStartクラス定数)
// -> 未指定の場合はCoroutineStart.Default(即時実行)
// block: 起動するコルーチン
サンプルコード
@UiThread
private fun asyncExecute() {
// アクティビティのコルーチンスコープによるコルーチンの起動
lifecycleScope.launch {
// コルーチン
// ワーカースレッドで動作する非同期処理
backgroundTaskRunner()
// UIスレッドで動作する非同期処理
postBackgroundTaskRunner()
}
}
コルーチンを利用した非同期処理の実装まとめ
// UIスレッドで動作する非同期処理を含む処理
@UiThread
private fun asyncExecute() {
// ライフサイクルに最適化されたスコープを利用してコルーチンを起動
lifecycleScope.launch {
// 非同期(=ワーカースレッド)で行う処理
backgroundTaskRunner()
// 非同期処理後に同期(=UIスレッド)で行う処理
postBackgroundTaskRunner()
}
}
// ワーカースレッドで動作する処理(非同期処理)
@WorkerThread
private suspend fun backgroundTaskRunner() {
withContext(Dispatchers.IO) {
... // 非同期処理
}
}
// 非同期処理後にUIスレッドで動作する処理(同期処理)
@UiThread
private fun postBackgroundTaskRunner() {
... // 同期処理
}