Jetpack Compose and Android Lifecycle

Jetpack Compose is Android's declarative UI toolkit: composable functions describe the UI for the current state, and Compose re-runs (recomposes) them when that state changes. That model removes a whole class of view-synchronization bugs — but the Android lifecycle still applies underneath. Activities are destroyed and recreated on configuration changes, apps are stopped when they leave the screen, and processes are killed in the background and restored later.

Lifecycle-aware Compose code puts each piece of state where it survives the events it must survive, collects data only while the screen is visible, and runs side effects with APIs that start and stop at the right time. This page builds a search screen that keeps its query across process death, stops collecting when backgrounded, and is tested for state restoration.

TL;DR

Quick Example

A view model that keeps the search query in saved state and exposes UI state as a StateFlow:

The screen collects state with lifecycle awareness and keeps UI-only state in rememberSaveable:

Core Concepts

Where State Lives

Saved state is limited in size — store IDs and small values, not lists of results, and reload data from the repository after restoration.

State Hoisting and Unidirectional Flow

Stateless composables take state and callbacks as parameters (SearchScreen), and a thin stateful wrapper connects them to a view model (SearchRoute). This makes screens previewable and testable without a view model, and keeps the data flow one-directional. See Mobile App Architecture.

Lifecycle-Aware Collection

collectAsState() keeps collecting while the composable is in the composition — including while the app is in the background. collectAsStateWithLifecycle() (from lifecycle-runtime-compose) collects only while the lifecycle is at least STARTED, which stops location updates, database observers, and network polling when the user leaves. In the view model, SharingStarted.WhileSubscribed(5_000) keeps upstream flows alive briefly across configuration changes but stops them when nobody is watching.

Side Effects

Composition can run many times, in any order, and may be discarded, so never start work, write to databases, or send analytics directly in a composable body.

Recomposition and Performance

Compose skips composables whose inputs have not changed. Pass stable, immutable data (data classes with val properties, immutable collections); read rapidly changing state (scroll offsets, animations) as late as possible, for example in lambdas or layout modifiers; give lazy list items stable keys; and use derivedStateOf when a value changes less often than the state it is computed from. Use the Layout Inspector's recomposition counts to find hot spots.

Configuration Changes and Process Death

On rotation, theme change, or window resize, the activity is recreated: composition restarts, remember values are lost, rememberSaveable values and view models survive. When the process is killed in the background, view models are lost too — only saved state and persisted data remain. Many apps break here: a detail screen that received a whole object through navigation cannot rebuild it, or a multi-step flow restarts. Pass IDs through navigation, keep screen inputs in SavedStateHandle, and reload data from repositories.

Testing

Test view models separately with a SavedStateHandle containing restored values, and run end-to-end process-death checks on devices. See Mobile Testing and Device Matrices.

Best Practices

Keep Composables Stateless Where Possible

Hoist state to view models or callers so screens are easy to preview, test, and reuse.

Collect With Lifecycle Awareness

Use collectAsStateWithLifecycle() for every flow a screen observes.

Save Inputs, Reload Data

Store the minimum needed to rebuild a screen in saved state, and reload the rest from repositories.

Treat Effects as Lifecycle-Bound Work

Use effect APIs with correct keys and cleanup; avoid work in composition.

Common Mistakes

Work Inside Composition

remember Where rememberSaveable Is Needed

Collecting in the Background

FAQ

What is the difference between remember and rememberSaveable?

remember keeps a value across recompositions but loses it when the activity is recreated. rememberSaveable also saves the value in the saved instance state, so it survives configuration changes and process death.

Does a ViewModel survive process death?

No. A view model survives configuration changes, but it is recreated after process death. Use SavedStateHandle for values needed to rebuild the screen.

Why use collectAsStateWithLifecycle?

It collects a flow only while the lifecycle is at least STARTED, so observation stops when the app is in the background, saving resources and avoiding work the user cannot see.

Where should side effects go in Compose?

In effect APIs such as LaunchedEffect, DisposableEffect, and lifecycle effects, or in event handlers using a remembered coroutine scope — never directly in a composable's body.

How do I test state restoration in Compose?

Use StateRestorationTester from the Compose testing library to emulate saving and restoring instance state, and verify that saved UI state comes back.

Related Topics

References