1. 始める前に
前の Codelab では、コルーチンについて学習しました。Kotlin プレイグラウンドを使用して、コルーチンで同時実行コードを記述しました。この Codelab では、Android アプリ内のコルーチンとそのライフサイクルに関する知識を利用します。新しいコルーチンを同時に起動するためのコードを追加し、それらのコルーチンをテストする方法を学習します。
前提条件
- Kotlin 言語の基本(関数やラムダを含む)に関する知識
- Jetpack Compose でレイアウトを作成できること
- Kotlin で単体テストを作成できること(ViewModel Codelab の単体テストを作成するを参照)
- スレッドと同時実行の仕組みに関する知識
- コルーチンと CoroutineScope に関する基本的な知識
作成するアプリの概要
- 2 人のプレーヤー間のレースの進行状況をシミュレートする Race Tracker アプリを作成します。このアプリを通して、コルーチンのさまざまな側面についてテストし、学習を深めることができます。
学習内容
- Android アプリのライフサイクルにおけるコルーチンの使用。
- 構造化された同時実行の原則。
- コルーチンをテストする単体テストを作成する方法。
必要なもの
- Android Studio の最新の安定版
2. アプリの概要
Race Tracker は、2 人のプレーヤーによる競走をシミュレートするアプリです。アプリ UI は、[Start / Pause] と [Reset] の 2 つのボタンと、ランナーの進行状況を示す 2 つの進行状況バーで構成されています。レースは、プレーヤー 1 と 2 がそれぞれ異なるスピードで「走る」という設定です。実際のレースでは、プレーヤー 2 がプレーヤー 1 の 2 倍の速さで進みます。
アプリでコルーチンを使用して、次のことを確認します。
- 両方のプレーヤーが同時に「競走」する。
- アプリの UI がレスポンシブで、ランナーの進行に合わせて進行状況バーが伸びていく。
スターター コードには、Race Tracker アプリ用の UI コードが含まれています。Codelab のこのパートは主に、Android アプリ内の Kotlin コルーチンに慣れることに焦点を当てます。
スターター コードを取得する
まず、スターター コードをダウンロードします。
または、GitHub リポジトリのクローンを作成してコードを入手することもできます。
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-race-tracker.git $ cd basic-android-kotlin-compose-training-race-tracker $ git checkout starter
スターター コードは Race Tracker GitHub リポジトリで確認できます。
スターター コードのチュートリアル
レースを始めるには、[Start] ボタンをクリックします。レース中は、[Start] ボタンが [Pause] ボタンに変わります。
このボタンを使用して、レースの一時停止と再開をいつでも行えます。
レース中は、進行状況バー(ステータス インジケーター)が各プレーヤーの進行状況を示します。コンポーズ可能な関数 StatusIndicator
が各プレーヤーの進行状況を表示します。この関数は LinearProgressIndicator
コンポーザブルを使用して、進行状況バーを表示します。進行状況の値はコルーチンを使用して更新します。
RaceParticipant
: 進行状況バーを伸ばすためのデータを提供します。このクラスは各プレーヤーの状態ホルダーで、参加者の name
、レース完了に到達する maxProgress
、進行状況バーを伸ばす遅延間隔、レース中の進行状況を示すcurrentProgress
、initialProgress
を更新します。
次のセクションでは、コルーチンを使用して、アプリの UI をブロックせずにレースの進行状況をシミュレートする機能を実装します。
3. レースの進行状況を実装する
run()
関数を使用してプレーヤーの currentProgress
を maxProgress
と比較し、レースの全体的な進行状況を表示します。また、suspend 関数(delay()
)を使用して、進行状況バーを伸ばすタイミングにわずかな遅延を追加します。この関数は別の suspend 関数 delay()
を呼び出しているため、suspend
関数である必要があります。また、この関数は、Codelab の後半でコルーチンから呼び出します。関数を実装する手順は次のとおりです。
- スターター コードの一部である
RaceParticipant
クラスを開きます。 RaceParticipant
クラス内で、run()
という名前の新しいsuspend
関数を定義します。
class RaceParticipant(
...
) {
var currentProgress by mutableStateOf(initialProgress)
private set
suspend fun run() {
}
...
}
- レースの進行状況をシミュレートするには、
currentProgress
が値maxProgress
(100
に設定される)に達するまで実行されるwhile
ループを追加します。
class RaceParticipant(
...
val maxProgress: Int = 100,
...
) {
var currentProgress by mutableStateOf(initialProgress)
private set
suspend fun run() {
while (currentProgress < maxProgress) {
}
}
...
}
currentProgress
の値はinitialProgress
(0
)に設定されています。参加者の進行状況をシミュレートするには、while ループでprogressIncrement
プロパティの値ずつcurrentProgress
の値を増やしていきます。progressIncrement
のデフォルト値は1
です。
class RaceParticipant(
...
val maxProgress: Int = 100,
...
private val progressIncrement: Int = 1,
private val initialProgress: Int = 0
) {
...
var currentProgress by mutableStateOf(initialProgress)
private set
suspend fun run() {
while (currentProgress < maxProgress) {
currentProgress += progressIncrement
}
}
}
- レースの進行状況バーをさまざまな間隔で伸ばすシミュレーションを行うには、suspend 関数
delay()
を使用します。progressDelayMillis
プロパティの値を引数として渡します。
suspend fun run() {
while (currentProgress < maxProgress) {
delay(progressDelayMillis)
currentProgress += progressIncrement
}
}
今追加したコードを見ると、次のスクリーンショットのように、Android Studio の delay()
関数の呼び出しの左側にアイコンが表示されます。
このアイコンは、関数が一時停止してから後で再開できる一次停止ポイントを示します。
次の図に示すように、コルーチンが遅延時間中に待機している間、メインスレッドはブロックされません。
コルーチンは、目的の間隔値を指定して delay()
関数を呼び出した後、実行を一時停止します(ただし、ブロックしません)。遅延時間後、コルーチンは実行を再開して currentProgress
プロパティの値を更新します。
4. レースを開始する
ユーザーが [Start] ボタンを押したとき、2 つのプレーヤー インスタンスでそれぞれ suspend 関数 run()
を呼び出して「レースを開始」する必要があります。そのためには、run()
関数を呼び出すコルーチンを起動します。
レースを開始するためのコルーチンを起動するときは、両方のプレーヤーについて次の点を確認する必要があります。
- [Start] ボタンをクリックすると(コルーチンが起動すると)、すぐに走行が開始する。
- [Pause] ボタンまたは [Reset] ボタンをクリックすると、それぞれレースを一時停止するかリセットする(コルーチンがキャンセルされる)。
- アプリを閉じると、キャンセルが適切に処理される(すべてのコルーチンがキャンセルされ、ライフサイクルにバインドされる)。
最初の Codelab で、suspend 関数は別の suspend 関数からしか呼び出せないことを学習しました。コンポーザブル内から suspend 関数を安全に呼び出すには、LaunchedEffect()
コンポーザブルを使用する必要があります。LaunchedEffect()
コンポーザブルはコンポジションに残っている限り、指定された suspend 関数を実行します。コンポーズ可能な関数 LaunchedEffect()
を使用すると、次のすべてを行うことができます。
LaunchedEffect()
コンポーザブルを使用すると、コンポーザブルから suspend 関数を安全に呼び出すことができます。LaunchedEffect()
関数は、コンポジションに入ると、コードブロックがパラメータとして渡されたコルーチンを起動します。コンポジションに残っている限り、指定された suspend 関数を実行します。ユーザーが Race Tracker アプリの [Start] ボタンをクリックすると、LaunchedEffect()
はコンポジションに入り、コルーチンを起動して進行状況を更新します。LaunchedEffect()
がコンポジションを出ると、コルーチンはキャンセルされます。アプリでユーザーが [Reset] ボタンまたは [Pause] ボタンをクリックすると、LaunchedEffect()
がコンポジションから削除され、それが起動したコルーチンもキャンセルされます。
RaceTracker アプリの場合、ディスパッチャを明示的に提供する必要はありません(LaunchedEffect()
によって処理されるため)。
レースを開始するには、プレーヤーごとに run()
関数を呼び出して、次の手順を行います。
com.example.racetracker.ui
パッケージ内のRaceTrackerApp.kt
ファイルを開きます。RaceTrackerApp()
コンポーザブルに移動し、raceInProgress
の定義の後の行にLaunchedEffect()
コンポーザブルの呼び出しを追加します。
@Composable
fun RaceTrackerApp() {
...
var raceInProgress by remember { mutableStateOf(false) }
LaunchedEffect {
}
RaceTrackerScreen(...)
}
playerOne
またはplayerTwo
のインスタンスが別のインスタンスに置き換えられた場合、LaunchedEffect()
は起動したコルーチンをいったんキャンセルして再起動する必要があります。そのためには、playerOne
オブジェクトとplayerTwo
オブジェクトをkey
としてLaunchedEffect
に追加します。テキスト値が変更されたときにText()
コンポーザブルが再コンポーズされる仕組みと同様に、LaunchedEffect()
のいずれかの重要な引数が変更されると、それが起動したコルーチンはいったんキャンセルされた後、再起動されます。
LaunchedEffect(playerOne, playerTwo) {
}
playerOne.run()
関数とplayerTwo.run()
関数の呼び出しを追加します。
@Composable
fun RaceTrackerApp() {
...
var raceInProgress by remember { mutableStateOf(false) }
LaunchedEffect(playerOne, playerTwo) {
playerOne.run()
playerTwo.run()
}
RaceTrackerScreen(...)
}
LaunchedEffect()
ブロックをif
条件でラップします。この状態の初期値はfalse
です。ユーザーが [Start] ボタンをクリックし、LaunchedEffect()
が実行されると、raceInProgress
状態の値がtrue
に更新されます。
if (raceInProgress) {
LaunchedEffect(playerOne, playerTwo) {
playerOne.run()
playerTwo.run()
}
}
raceInProgress
フラグをfalse
に更新してレースを終了します。この値は、ユーザーが [Pause] ボタンをクリックした場合でもfalse
に設定されます。この値がfalse
に設定されると、LaunchedEffect()
により、起動されたすべてのコルーチンは確実にキャンセルされます。
LaunchedEffect(playerOne, playerTwo) {
playerOne.run()
playerTwo.run()
raceInProgress = false
}
- アプリを実行し、[Start] ボタンをクリックします。プレーヤー 2 がレースを開始する前に、プレーヤー 1 がレースを完了していることがわかります。次の動画をご覧ください。
公平なレースではないようです。次のセクションでは、同時実行タスクを起動して、両方のプレーヤーが同時に走行できるようにする方法を学習し、コンセプトを理解してこの動作を実装します。
5. 構造化された同時実行
コルーチンを使用してコードを記述する方法は、構造化された同時実行と呼ばれます。この種類のプログラミングにより、コードの読みやすさが改善し、開発時間が短縮します。構造化された同時実行とは、コルーチンに階層があるということです。つまり、タスクによってサブタスクが起動され、次いでサブタスクが起動されます。この階層の単位はコルーチン スコープと呼ばれます。コルーチン スコープは常にライフサイクルに関連付ける必要があります。
コルーチン API は、この構造化された同時実行に準拠した設計となっています。suspend としてマークされていない関数から suspend 関数を呼び出すことはできません。この制限により、launch
などのコルーチン ビルダーから suspend 関数を確実に呼び出すことができます。これらのビルダーは、今度は CoroutineScope
に関連付けられます。
6. 同時実行タスクを起動する
- 両方の参加者を同時に走行させるには、2 つのコルーチンを別々に起動し、
run()
関数の各呼び出しをこれらのコルーチン内に移動します。playerOne.run()
の呼び出しをlaunch
ビルダーでラップします。
LaunchedEffect(playerOne, playerTwo) {
launch { playerOne.run() }
playerTwo.run()
raceInProgress = false
}
- 同様に、
playerTwo.run()
関数の呼び出しをlaunch
ビルダーでラップします。この変更により、アプリは同時に実行される 2 つのコルーチンを起動します。両方のプレーヤーが同時に走行できるようになりました。
LaunchedEffect(playerOne, playerTwo) {
launch { playerOne.run() }
launch { playerTwo.run() }
raceInProgress = false
}
- アプリを実行し、[Start] ボタンをクリックします。レースが開始される見込みでしたが、予想とは異なりボタンのテキストが [Start] に戻ります。
両方のプレーヤーが走行を完了したら、Race Tracker アプリは [Pause] ボタンのテキストを [Start] にリセットする必要があります。ただしここでは、プレーヤーのレースの完了を待つのではなく、コルーチンが起動されるとすぐにアプリが raceInProgress
を更新します。
LaunchedEffect(playerOne, playerTwo) {
launch {playerOne.run() }
launch {playerTwo.run() }
raceInProgress = false // This will update the state immediately, without waiting for players to finish run() execution.
}
次の理由により、raceInProgress
フラグがすぐに更新されます。
launch
ビルダー関数は、playerOne.run()
を実行するコルーチンを起動し、すぐにコードブロックの次の行を実行します。playerTwo.run()
関数を実行する 2 番目のlaunch
ビルダー関数でも、同じ実行フローが発生します。- 2 番目の
launch
ビルダーが返されると、すぐにraceInProgress
フラグが更新されます。この場合、ボタンはすぐに [スタート] ボタンとなり、レースは開始されません。
コルーチンのスコープ
coroutineScope
suspend 関数は CoroutineScope
を作成し、指定された suspend ブロックを現在のスコープで呼び出します。スコープは、LaunchedEffect()
スコープから coroutineContext
を継承します。
このスコープは、指定されたブロックとそのすべての子コルーチンが完了するとすぐに返されます。RaceTracker
アプリの場合、両方の参加者オブジェクトが run()
関数の実行を終了すると、返されます。
raceInProgress
フラグを更新する前にplayerOne
とplayerTwo
のrun()
関数の実行が完了するように、両方の起動ビルダーをcoroutineScope
ブロックでラップします。
LaunchedEffect(playerOne, playerTwo) {
coroutineScope {
launch { playerOne.run() }
launch { playerTwo.run() }
}
raceInProgress = false
}
- エミュレータまたは Android デバイスでアプリを実行します。次の画面が表示されます。
- [Start] ボタンをクリックします。プレーヤー 2 はプレーヤー 1 よりも速く走行します。レースが終了すると(両方のプレーヤーが 100% の進行状況に達すると)、[Pause] ボタンが [Start] ボタンに変わります。[Reset] ボタンをクリックすると、レースをリセットしてシミュレーションを再実行できます。レースを次の動画で示します。
次の図に実行フローを示します。
LaunchedEffect()
ブロックが実行されると、制御はcoroutineScope{..}
ブロックに移ります。coroutineScope
ブロックは、両方のコルーチンを同時に起動し、実行が完了するのを待ちます。- 実行が完了すると、
raceInProgress
フラグが更新されます。
coroutineScope
ブロックは、ブロック内のすべてのコードの実行が完了した後にのみ、実行を継続します。ブロックの外のコードでは、同時実行の有無は実装の詳細にすぎません。このコーディング スタイルは、同時実行プログラミングに対する構造化されたアプローチを提供するもので、構造化された同時実行と呼ばれます。
レースの完了後に [Reset] ボタンをクリックすると、コルーチンがキャンセルされ、両方のプレーヤーの進行状況が 0
にリセットされます。
ユーザーが [Reset] ボタンをクリックしたときにコルーチンがどのようにキャンセルされるかを確認するには、次の手順を行います。
- 次のコードに示すように、
run()
メソッドの本体を try-catch ブロックでラップします。
suspend fun run() {
try {
while (currentProgress < maxProgress) {
delay(progressDelayMillis)
currentProgress += progressIncrement
}
} catch (e: CancellationException) {
Log.e("RaceParticipant", "$name: ${e.message}")
throw e // Always re-throw CancellationException.
}
}
- アプリを実行し、[Start] ボタンをクリックします。
- 進行状況の数値が増えたら、[Reset] ボタンをクリックします。
- 次のメッセージが Logcat に出力されていることを確認します。
Player 1: StandaloneCoroutine was cancelled Player 2: StandaloneCoroutine was cancelled
7. コルーチンをテストする単体テストを作成する
コルーチンを使用する単体テストのコードの実行は、非同期、かつ複数のスレッド間で行われる可能性があるため、特別な注意が必要です。
テストで suspend 関数を呼び出すには、コルーチン内で行う必要があります。JUnit テスト関数自体は suspend 関数ではないため、runTest
コルーチン ビルダーを使用する必要があります。このビルダーは kotlinx-coroutines-test
ライブラリの一部であり、テストを実行するように設計されています。ビルダーは新しいコルーチンでテスト本体を実行します。
runTest
は kotlinx-coroutines-test
ライブラリの一部であるため、その依存関係を追加する必要があります。
依存関係を追加するには、次の手順を行います。
- [Project] ペインの
app
ディレクトリにある、アプリ モジュールのbuild.gradle.kts
ファイルを開きます。
- ファイル内で、
dependencies{}
ブロックが見つかるまで下にスクロールします。 testImplementation
構成ファイルを使用して依存関係をkotlinx-coroutines-test
ライブラリに追加します。
plugins {
...
}
android {
...
}
dependencies {
...
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
}
- 次のスクリーンショットに示すように、build.gradle.kts ファイルの上部にある通知バーで [Sync Now] をクリックし、インポートとビルドを終了します。
ビルドが完了したら、テストの作成を開始できます。
レースの開始と終了の単体テストを実装する
レースの各段階でレースの進行状況が正しく更新されるようにするには、単体テストでさまざまなシナリオに対応する必要があります。この Codelab では、次の 2 つのシナリオについて説明します。
- レース開始後の進行状況。
- レース終了後の進行状況。
レースの開始後にレースの進行状況が正しく更新されるかどうかを確認するには、raceParticipant.progressDelayMillis
期間が経過した後に現在の進行状況が 1 に設定されていることをアサートします。
テストシナリオを実装する手順は次のとおりです。
- テスト ソースセットの下にある
RaceParticipantTest.kt
ファイルに移動します。 - テストを定義するには、
raceParticipant
定義の後にraceParticipant_RaceStarted_ProgressUpdated()
関数を作成し、@Test
アノテーションを付けます。テストブロックはrunTest
ビルダーに配置する必要があるため、式の構文を使用してrunTest()
ブロックをテスト結果として返します。
class RaceParticipantTest {
private val raceParticipant = RaceParticipant(
...
)
@Test
fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
}
}
- 読み取り専用の
expectedProgress
変数を追加し、1
に設定します。
@Test
fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
val expectedProgress = 1
}
- レースの開始をシミュレートするには、
launch
ビルダーを使用して新しいコルーチンを起動し、raceParticipant.run()
関数を呼び出します。
@Test
fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
val expectedProgress = 1
launch { raceParticipant.run() }
}
raceParticipant.progressDelayMillis
プロパティの値によって、レースの進行状況が更新されるまでの時間が決まります。progressDelayMillis
時間が経過した後に進行状況をテストするには、テストになんらかの遅延を追加します。
advanceTimeBy()
ヘルパー関数を使用して、時間をraceParticipant.progressDelayMillis
の値だけ進めます。advanceTimeBy()
関数を使用してテスト実行時間を短縮できます。
@Test
fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
val expectedProgress = 1
launch { raceParticipant.run() }
advanceTimeBy(raceParticipant.progressDelayMillis)
}
advanceTimeBy()
は指定された時間にスケジュール設定されたタスクを実行しないので、runCurrent()
関数を呼び出す必要があります。この関数は、現在の時刻に保留中のタスクをすべて実行します。
@Test
fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
val expectedProgress = 1
launch { raceParticipant.run() }
advanceTimeBy(raceParticipant.progressDelayMillis)
runCurrent()
}
- 進行状況が確実に更新されるようにするには、
assertEquals()
関数の呼び出しを追加して、raceParticipant.currentProgress
プロパティの値がexpectedProgress
変数の値と一致するかどうかを確認します。
@Test
fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
val expectedProgress = 1
launch { raceParticipant.run() }
advanceTimeBy(raceParticipant.progressDelayMillis)
runCurrent()
assertEquals(expectedProgress, raceParticipant.currentProgress)
}
- テストを実行して、合格することを確認します。
レースの終了後、レースの進行状況が正しく更新されているかどうかを確認するには、レースの終了時に現在の進行状況が 100
に設定されていることをアサートします。
テストを実装する手順は次のとおりです。
raceParticipant_RaceStarted_ProgressUpdated()
テスト関数の後に、raceParticipant_RaceFinished_ProgressUpdated()
関数を作成し、@Test
アノテーションを付けます。この関数は、runTest{}
ブロックからテスト結果を返します。
class RaceParticipantTest {
...
@Test
fun raceParticipant_RaceStarted_ProgressUpdated() = runTest {
...
}
@Test
fun raceParticipant_RaceFinished_ProgressUpdated() = runTest {
}
}
launch
ビルダーを使用して新しいコルーチンを起動し、そのraceParticipant.run()
関数の呼び出しを追加します。
@Test
fun raceParticipant_RaceFinished_ProgressUpdated() = runTest {
launch { raceParticipant.run() }
}
- レースの終了をシミュレートするには、
advanceTimeBy()
関数を使用して、ディスパッチ時間をraceParticipant.maxProgress * raceParticipant.progressDelayMillis
だけ進めます。
@Test
fun raceParticipant_RaceFinished_ProgressUpdated() = runTest {
launch { raceParticipant.run() }
advanceTimeBy(raceParticipant.maxProgress * raceParticipant.progressDelayMillis)
}
runCurrent()
関数の呼び出しを追加して、保留中のタスクを実行します。
@Test
fun raceParticipant_RaceFinished_ProgressUpdated() = runTest {
launch { raceParticipant.run() }
advanceTimeBy(raceParticipant.maxProgress * raceParticipant.progressDelayMillis)
runCurrent()
}
- 進行状況が正しく更新されるようにするには、
assertEquals()
関数の呼び出しを追加してraceParticipant.currentProgress
プロパティの値が100
と等しいかどうかを確認します。
@Test
fun raceParticipant_RaceFinished_ProgressUpdated() = runTest {
launch { raceParticipant.run() }
advanceTimeBy(raceParticipant.maxProgress * raceParticipant.progressDelayMillis)
runCurrent()
assertEquals(100, raceParticipant.currentProgress)
}
- テストを実行して、合格することを確認します。
課題に挑戦しましょう
ViewModel の単体テストを作成するの Codelab で説明したテスト戦略を利用します。ハッピーパス、エラーケース、境界ケースを網羅するテストを追加します。
作成したテストと、解答コードで使用できるテストを比較します。
8. 解答コードを取得する
この Codelab の完成したコードをダウンロードするには、以下の git コマンドを使用します。
git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-race-tracker.git cd basic-android-kotlin-compose-training-race-tracker
または、リポジトリを ZIP ファイルとしてダウンロードし、Android Studio で開くこともできます。
解答コードを確認する場合は、GitHub で表示します。
9. おわりに
お疲れさまでした。ここまで、コルーチンを使用して同時実行を処理する方法について学習しました。コルーチンは、メインスレッドをブロックしてアプリの応答を止める可能性のある長時間実行タスクの管理に役立ちます。また、コルーチンをテストする単体テストを作成する方法についても学習しました。
コルーチンには、次のような機能があります。
- 読みやすさ: コルーチンを使用して記述するコードにより、コード行の実行順序がわかりやすくなります。
- Jetpack の統合: Compose や ViewModel など、Jetpack ライブラリの多くには、コルーチンを全面的にサポートする拡張機能が用意されています。一部のライブラリでは、構造化された同時実行に使用できる独自のコルーチン スコープも用意されています。
- 構造化された同時実行: コルーチンによって同時実行コードを安全かつ簡単に実装できるようにし、不要なボイラープレート コードを排除し、アプリで起動されたコルーチンの紛失や漏洩を防ぎます。
概要
- コルーチンを使用すると、新しいプログラミング スタイルを学習することなく、同時実行する長時間実行コードを作成できます。設計上、コルーチンの実行は順次行われます。
suspend
キーワードは関数または関数型をマークして、一連のコード命令の実行、一時停止、再開が可能であることを示すために使用されます。suspend
関数は、別の suspend 関数からのみ呼び出すことができます。- 新しいコルーチンは、
launch
またはasync
ビルダー関数を使用して開始できます。 - コルーチンのコンテキスト、コルーチン ビルダー、ジョブ、コルーチン スコープ、ディスパッチャは、コルーチンを実装するための主要なコンポーネントです。
- コルーチンは、ディスパッチャを使用して実行に使用するスレッドを決定します。
- ジョブは、コルーチンのライフサイクルを管理し、親子関係を維持することで、構造化された同時実行を確保するうえで重要な役割を果たします。
CoroutineContext
は、ジョブとコルーチン ディスパッチャを使用してコルーチンの動作を定義します。CoroutineScope
は、そのジョブを通じてコルーチンの存続期間を制御し、子とその子にキャンセルやその他のルールを再帰的に適用します。- 起動、完了、キャンセル、失敗の 4 つは、コルーチンの実行における一般的なオペレーションです。
- コルーチンは構造化された同時実行の原則に従います。