Kotlin のコルーチンには、非同期コードを記述できる API が用意されています。Kotlin コルーチンを使用すると、CoroutineScope を定義して、コルーチンを実行するタイミングを管理できます。非同期オペレーションはそれぞれ、特定のスコープで実行されます。
ライフサイクル対応コンポーネントは、アプリ内の論理スコープ用コルーチンを万全の体制でサポートしています。このドキュメントでは、ライフサイクル対応コンポーネントでコルーチンを効果的に使用する方法について説明します。
依存関係を追加する
このトピックで説明する組み込みのコルーチン スコープは、Lifecycle API に含まれています。これらのスコープを使用するときは、必ず適切な依存関係を追加してください。
- Compose の ViewModel ユーティリティには、
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version")を使用します。 - Compose のライフサイクル ユーティリティには、
implementation("androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version")を使用します。
ライフサイクル対応コルーチン スコープ
Compose ライブラリと Lifecycle ライブラリには、アプリで使用できる次の組み込みスコープが用意されています。
ViewModelScope
ViewModelScope は、アプリで ViewModel ごとに定義されます。このスコープ内で起動されたすべてのコルーチンは、ViewModel が消去されると自動的にキャンセルされます。ViewModel がアクティブな場合にのみ行う必要がある作業があるとき、コルーチンが役に立ちます。たとえばレイアウト用のデータを計算している場合、作業を ViewModel にスコープする必要があるため、ViewModel が消去されると、作業はリソースの消費を避けるために自動的にキャンセルされます。
ViewModel の CoroutineScope にアクセスするには、次の例に示すように ViewModel の viewModelScope プロパティを使用します。
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
}
}
より高度なユースケースでは、カスタムの CoroutineScope を ViewModel のコンストラクタに直接渡して、デフォルトの viewModelScope を置き換えることができます。このアプローチでは、特に次の点で管理性と柔軟性が向上します。
テスト:
TestScopeを挿入できるため、単体テストで時間を制御し、コルーチンの動作を検証しやすくなります。カスタム構成: ViewModel が処理を開始する前に、特定の
CoroutineDispatcher(重い計算用のDispatchers.Defaultなど)またはカスタムCoroutineExceptionHandlerを使用してスコープを構成できます。
コンポジション バインド スコープ
アニメーション、ネットワーク呼び出し、タイマーなどの副作用は、コンポーザブルのライフサイクルにスコープする必要があります。このようにすると、コンポーザブルが画面から離れる(コンポジションを終了する)ときに、実行中のコルーチンが自動的にキャンセルされ、メモリリークを防ぐことができます。
Compose は、コンポジションのスコープを宣言的に処理するための LaunchedEffect API を提供します。
LaunchedEffect は、一時停止関数を実行できる CoroutineScope を作成します。スコープは、ホスト アクティビティのライフサイクルではなく、コンポーザブルのコンポジション ライフサイクルに結び付けられます。
- Enter: コンポーザブルがコンポジションに入ると、コルーチンが開始されます。
- 終了: コンポーザブルがコンポジションを離れると、コルーチンはキャンセルされます。
- 再起動:
LaunchedEffectに渡されたキーが変更されると、既存のコルーチンがキャンセルされ、新しいコルーチンが起動されます。
次の例は、LaunchedEffect を使用してパルス アニメーションを作成する方法を示しています。コルーチンはコンポジション内のコンポーザブルの存在に結び付けられ、構成の変更に対応します。
// Allow the pulse rate to be configured, so it can be sped up if the user is running // out of time var pulseRateMs by remember { mutableLongStateOf(3000L) } val alpha = remember { Animatable(1f) } LaunchedEffect(pulseRateMs) { // Restart the effect when the pulse rate changes while (isActive) { delay(pulseRateMs) // Pulse the alpha every pulseRateMs to alert the user alpha.animateTo(0f) alpha.animateTo(1f) } }
LaunchedEffect の詳細については、Compose における副作用をご覧ください。
ライフサイクル対応フロー収集
Jetpack Compose でフローを安全に収集するには、collectAsStateWithLifecycle API を使用します。この単一の関数は、Flow を Compose State オブジェクトに変換し、ライフサイクル サブスクリプションを自動的に管理します。デフォルトでは、ライフサイクルが STARTED のときに収集が開始され、ライフサイクルが STOPPED のときに収集が停止します。このデフォルトの動作をオーバーライドするには、必要なライフサイクル メソッド(Lifecycle.State.RESUMED など)を指定して minActiveState パラメータを渡します。
次の例は、コンポーザブルで ViewModel の StateFlow を収集する方法を示しています。
@Composable private fun ConversationScreen( conversationViewModel: ConversationViewModel = viewModel() ) { val messages by conversationViewModel.messages.collectAsStateWithLifecycle() ConversationScreen( messages = messages, onSendMessage = { message: Message -> conversationViewModel.sendMessage(message) } ) } @Composable private fun ConversationScreen( messages: List<Message>, onSendMessage: (Message) -> Unit ) { MessagesList(messages, onSendMessage) /* ... */ }
複数のフローの並列コレクション
Compose では、複数の状態変数を宣言することで、複数のフローを並行して収集できます。collectAsStateWithLifecycle は独自の基盤となるスコープを管理するため、並列コレクションは自動的に処理されます。
@Composable
fun DashboardScreen(viewModel: DashboardViewModel = viewModel()) {
// Both flows are collected safely in parallel and will emit updates when either changes, the composables will recompose
val userData by viewModel.userFlow.collectAsStateWithLifecycle()
val feedData by viewModel.feedFlow.collectAsStateWithLifecycle()
// ...
}
Flow を使用して値を非同期で計算する
値を非同期で計算する必要がある場合は、stateIn 演算子とともに StateFlow を使用します。
次のスニペットでは、StateFlow に変換された標準の Flow を使用しています。WhileSubscribed(5000) パラメータは、UI が消えてから 5 秒間サブスクリプションをアクティブな状態に保ち、構成の変更を処理します。
val uiState: StateFlow<Result> = flow {
emit(repository.fetchData())
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = Result.Loading
)
collectAsStateWithLifecycle を使用して、収集した値を Compose の State に変換します。これにより、データが変更されるたびに UI をリアクティブに更新できます。
状態について詳しくは、状態と Jetpack Compose をご覧ください。
参考情報
コンテンツの閲覧
サンプル
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- ライフサイクル対応コンポーネントによるライフサイクルへの対応
- ページング データを読み込む、表示する