Lifecycle in Jetpack Compose Part of Android Jetpack.
Lifecycle-aware components perform actions in response to a change in the
lifecycle status of the host activity. The
androidx.lifecycle.compose
artifact provides dedicated APIs that automatically clean up resources when they
leave the screen or when the application goes into the background.
Key APIs include the following:
- Flows for the current
Lifecycle.State. LifecycleEffects, which lets you run a block based on a specificLifecycle.Event.
These integrations provide convenient hooks to manage lifecycles within the Compose hierarchy. This document outlines how you can use them in your app.
Collect lifecycle state with flows
Lifecycle exposes a currentStateFlow property that provides the current
Lifecycle.State as a Kotlin StateFlow. You can collect this Flow as
State. This allows your app to read changes in the Lifecycle during
composition.
val lifecycleOwner = LocalLifecycleOwner.current
val stateFlow = lifecycleOwner.lifecycle.currentStateFlow
…
val currentLifecycleState by stateFlow.collectAsState()
The preceding example is accessible using the lifecycle-common module. The
currentStateAsState() method is available in the lifecycle-runtime-compose
module, which lets you conveniently read the current Lifecycle state with a
single line. The following example demonstrates this:
val lifecycleOwner = LocalLifecycleOwner.current
val currentLifecycleState = lifecycleOwner.lifecycle.currentStateAsState()
Run code on lifecycle events
Instead of creating a separate class that implements
DefaultLifecycleObserver
and manually adding it to the lifecycle, you can declare lifecycle logic inline
using specific effects. LifecycleEffects lets you run a block when a
particular Lifecycle.Event occurs directly within the composition.
LifecycleEventEffect
Use LifecycleEventEffect to run a block of code
when a specific event occurs. This is best for one-shot events like logging or
analytics, where neither success nor an immediate outcome is needed.
@Composable
fun AnalyticsTracker(screenName: String) {
// Log an event when the app receives ON_RESUME (e.g. comes to foreground)
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
Analytics.logView(screenName)
}
}
LifecycleStartEffect
For paired start/stop operations that need to run while the app is started
(visible) and clean up when the app is stopped (in the background), use
LifecycleStartEffect.
Similar to other Compose effects (like LaunchedEffect), LifecycleStartEffect
accepts keys. When the key changes, it triggers the block to run again.
When a Lifecycle.Event.ON_STOP event occurs or the effect exits the
composition, it executes the onStopOrDispose block to clean up any work that
was part of the starting block.
@Composable
fun LocationMonitor(locationManager: LocationManager) {
// Starts monitoring when ON_START is dispatched
// Stops monitoring when ON_STOP is dispatched
// (or the composable leaves the screen)
LifecycleStartEffect(locationManager) {
val listener = LocationListener { location ->
/* update UI */
}
locationManager.requestLocationUpdates(listener)
// The cleanup block automatically runs on ON_STOP or on disposal
onStopOrDispose {
locationManager.removeUpdates(listener)
}
}
}
For information about other types of side effects, see Side-effects in Compose.
LifecycleResumeEffect
The LifecycleResumeEffect works in the same way as the LifecycleStartEffect,
but it is tied to the Lifecycle.Event.ON_RESUME event instead. It also
provides an onPauseOrDispose block that performs the cleanup when ON_PAUSE
is dispatched or the composable leaves the screen.
This API is useful for resources that need to be active only when the user is interacting with the app—for example, cameras or animations.
@Composable
fun CameraPreview(cameraController: CameraController) {
LifecycleResumeEffect(cameraController) {
cameraController.startPreview()
onPauseOrDispose {
cameraController.stopPreview()
}
}
}
Access the LifecycleOwner
In Compose, the
LifecycleOwner is
implicitly available through the CompositionLocal named LocalLifecycleOwner. By
default, the root host of your composition hierarchy provides this owner.
val lifecycleOwner = LocalLifecycleOwner.current
For many apps, inspecting this default owner or passing it to lifecycle-aware
effects is sufficient. However, for custom navigation or complex layouts, you
might want to create your own LifecycleOwner to scope lifecycle states to
specific sections of the UI. For example, navigation libraries (like Navigation
3) do this automatically to give each
individual screen its own lifecycle.
Create a custom LifecycleOwner
The rememberLifecycleOwner() API lets you create and remember a custom
LifecycleOwner. This is especially useful for components like a
HorizontalPager, where you want only the visible, settled page to be
RESUMED, while setting a maxState of STARTED for the adjacent, off-screen
pages.
val pagerState = rememberPagerState(pageCount = { 10 })
HorizontalPager(state = pagerState) { pageNum ->
val pageLifecycleOwner = rememberLifecycleOwner(
maxState = if (pagerState.settledPage == pageNum) {
Lifecycle.State.RESUMED
} else {
Lifecycle.State.STARTED
}
)
CompositionLocalProvider(LocalLifecycleOwner provides pageLifecycleOwner) {
// Your pages here. Their lifecycle-aware components respect the
// custom maxState defined above.
}
}
For more information on CompositionLocal, see Locally scoped data with
CompositionLocal.
Best practices for lifecycle-aware components
- Keep your UI controllers as lean as possible. They
should not try to acquire their own data; instead, use a
ViewModelto do that, and observe aStateFlowobject to reflect the changes back to the UI. - Try to write data-driven UIs where your UI controller’s responsibility is to
update the UI as data changes, or notify user actions back to the
ViewModel. - Put your data logic in your
ViewModelclass.ViewModelshould serve as the connector between your UI controller and the rest of your app. Be careful though, it isn'tViewModel's responsibility to fetch data (for example, from a network). Instead,ViewModelshould call the appropriate component to fetch the data, then provide the result back to the UI controller. - Use Kotlin coroutines to manage long-running tasks and other operations that can run asynchronously.
- Keep start/stop logic inside the composable that actually needs it. This way, the logic is automatically removed if that specific UI element is removed from the screen (for example, inside a navigation graph or when visibility is conditional).
- Use
collectAsStateWithLifecyclefor data. Do not manually start or stopFlowcollection based on lifecycle events. Instead, usecollectAsStateWithLifecycleto convert streams into UI state efficiently. This saves battery and resources becauseFlows are paused when the app is in the background.
For more information on Flows, see Other supported types of state.
Use cases for lifecycle-aware components
Lifecycle-aware components can make it much easier for you to manage lifecycles in a variety of cases. A few examples are:
- Switching between coarse and fine-grained location updates. Use
LifecycleStartEffectto enable fine-grained location updates while your app is visible (ON_START), and automatically clean up the listener or switch to coarse-grained updates when the app is in the background (ON_STOP). - Stopping and starting video buffering. Use
LifecycleResumeEffectto defer actual video playback until the app is fully in the foreground and interactive (ON_RESUME), and to make sure that playback pauses and releases resources when the app is backgrounded (ON_PAUSE). - Starting and stopping network streaming. Use
collectAsStateWithLifecycleto observe continuous streams of data (like a Kotlin Flow from a network socket). This gives you live updating while an app is in the foreground and automatically cancels collection when the app goes into the background. - Pausing and resuming heavy tasks. Use
LifecycleResumeEffectto handle pausing heavy visual updates when the app is in the background, and resume them after the app is in the foreground.
Handling ON_STOP events safely
Compose is designed to handle ON_STOP events safely.
- State is safe: You can update
MutableState(for example, withuiState.value = ...) at any time, even when the app is in the background. Compose waits until the app is visible to render the changes. - Automatic cleanup: With effects like
LifecycleStartEffect, your cleanup block (onStopOrDispose) runs exactly when the lifecycle moves toSTOPPED. This prevents you from holding heavy resources (like camera or location) while the app is in the background.
For more information on MutableState, see State and Jetpack Compose.
Additional resources
To learn more about handling lifecycles with lifecycle-aware components, consult the following additional resources.
Views content
Recommended for you
- Note: link text is displayed when JavaScript is off
- LiveData overview
- Use Kotlin coroutines with lifecycle-aware components
- Saved State module for ViewModel