Kotlin coroutines provide an API that lets you write asynchronous
code. With Kotlin coroutines, you can define a CoroutineScope, which
helps you to manage when your coroutines should run. Each asynchronous operation
runs within a particular scope.
Lifecycle-aware components provide first-class support for coroutines for logical scopes in your app. This document explains how to use coroutines effectively with lifecycle-aware components.
Add dependencies
The built-in coroutine scopes described in this topic are contained in the Lifecycle API. Be sure to add the appropriate dependencies when using these scopes.
- For ViewModel utilities in Compose, use
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"). - For Lifecycle utilities in Compose, use
implementation("androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version").
Lifecycle-aware coroutine scopes
Compose and the Lifecycle libraries provide the following built-in scopes that you can use in your app.
ViewModelScope
A ViewModelScope is defined for each ViewModel in your app. Any
coroutine launched in this scope is automatically canceled if the ViewModel is
cleared. Coroutines are useful here for when you have work that needs to be done
only if the ViewModel is active. For example, if you are computing some data
for a layout, you should scope the work to the ViewModel so that if the
ViewModel is cleared, the work is canceled automatically to avoid consuming
resources.
You can access the CoroutineScope of a ViewModel through the
viewModelScope property of the ViewModel, as shown in the following example:
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
}
}
For more advanced use cases, you can pass a custom CoroutineScope directly
into the ViewModel's constructor to replace the default viewModelScope. This
approach offers more control and flexibility, particularly for:
Testing: It lets you inject a
TestScope, making it easier to control time and verify coroutine behavior in unit tests.Custom configuration: You can configure the scope with a specific
CoroutineDispatcher(likeDispatchers.Defaultfor heavy computation) or a customCoroutineExceptionHandlerbefore the ViewModel even starts its work.
Composition-bound scopes
Side effects such as animations, network calls, or timers must be scoped to the lifecycle of the composable. This way, when a composable leaves the screen (exits the composition), any running coroutines are automatically canceled to prevent memory leaks.
Compose provides the LaunchedEffect API to handle Composition scoping
declaratively.
LaunchedEffect creates a CoroutineScope that lets you run suspend
functions. The scope is tied to the Composition lifecycle of the composable, not
the Lifecycle of the host Activity.
- Enter: The coroutine starts when the composable enters the composition.
- Exit: The coroutine is canceled when the composable leaves the composition.
- Relaunch: If any key passed to
LaunchedEffectchanges, the existing coroutine is canceled and a new one is launched.
The following example demonstrates how to use LaunchedEffect to create a
pulsing animation. The coroutine is tied to the composable's presence in the
composition and reacts to configuration changes:
// 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) } }
For more information on LaunchedEffect, see Side-effects in Compose.
Lifecycle-aware flow collection
To safely collect flows in Jetpack Compose, use the
collectAsStateWithLifecycle API. This single function converts a Flow
into a Compose State object and automatically manages the lifecycle
subscription for you. By default, collection begins when the lifecycle is
STARTED and stops when the lifecycle is STOPPED. To override this default
behavior, pass in the minActiveState parameter with the lifecycle method you
want, like Lifecycle.State.RESUMED.
The following example demonstrates how to collect a ViewModel's StateFlow in a
composable:
@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) /* ... */ }
Parallel collection of multiple flows
In Compose, you can collect multiple flows in parallel by declaring multiple
state variables. Because collectAsStateWithLifecycle manages its own
underlying scope, parallel collection is handled automatically:
@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()
// ...
}
Calculate values asynchronously using Flows
When you need to calculate values asynchronously, use StateFlow with the
stateIn operator.
The following snippet uses a standard Flow converted to a StateFlow. The
WhileSubscribed(5000) parameter keeps the subscription active for five
seconds after the UI disappears to handle configuration changes.
val uiState: StateFlow<Result> = flow {
emit(repository.fetchData())
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = Result.Loading
)
Use collectAsStateWithLifecycle to convert the collected values into Compose
State, so that your UI can reactively update whenever the data changes.
For more information about state, see State and Jetpack Compose.
Additional resources
Views content
Samples
Recommended for you
- Note: link text is displayed when JavaScript is off
- Handling Lifecycles with Lifecycle-Aware Components
- Load and display paged data