Lifecycle & State Restoration: learning goals
Distinguish a recomposition from activity recreation, system process death and a fresh launch. Store each piece of state according to how long it must remain meaningful, then verify those choices with separate experiments.
Project setup and scope
Build the mental model
Composition lifetime, Activity lifetime and process lifetime are separate. A recomposition can rerun a function while retaining remembered values. Rotation commonly recreates an Activity, while a ViewModel owned by the retained store remains available. A killed process loses ordinary memory, including that ViewModel.
rememberSaveable and SavedStateHandle store small reconstruction inputs through Android's saved-state mechanism. They help when the system recreates an existing task; they do not guarantee recovery after every task dismissal, force-stop, or user-cleared app data event. A durable session record belongs in Room or another persistent store.
The useful question is not 'How do I keep every object alive?' but 'What minimum input lets me reconstruct the screen?' Save a search query and selected ID, then reload records and recompute filtered results. Large lists and bitmaps can exceed saved-state limits and become stale copies.
collectAsStateWithLifecycle makes a lifecycle-aware subscription for UI collection. It does not necessarily stop all upstream work: sharing policy and other collectors determine what the producer continues doing. Choose both observation and production lifetimes deliberately.
Choose the right approach
| Need | Approach |
|---|---|
| Temporary expansion while displayed | remember when losing it on leaving composition is acceptable. |
| Short editable UI input | rememberSaveable for local UI state, or SavedStateHandle when used by screen logic. |
| Screen business state during rotation | ViewModel; reconstruct needed state after process recreation. |
| User-created records across launches | Room/files; persist at a defined transaction boundary. |
| Continuous UI observation | collectAsStateWithLifecycle plus a deliberate upstream sharing policy. |
Predict and trace the behavior
Lifecycle & State Restoration
Predict what happens next. Follow the diagram, then explain the change in your own words.
State down · events up
current stateOne owner updates the value. The UI receives a value and sends actions back through callbacks.
Explore this general model independently. The lesson sequence below follows its own example.
Read: The UI receives a value from its state owner.
Type query
The user types 'Compose'. The screen sends the input to its ViewModel.
Worked Kotlin example
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.StateFlow
class SearchViewModel(private val savedState: SavedStateHandle) : ViewModel() {
val query: StateFlow<String> = savedState.getStateFlow("query", "")
fun onQueryChanged(value: String) {
savedState["query"] = value
}
}
// In a composable, with lifecycle-runtime-compose and runtime getValue imports:
// val query by viewModel.query.collectAsStateWithLifecycle()
// SearchField(query = query, onQueryChanged = viewModel::onQueryChanged)
// Reload/derive actual search results from the repository, not a saved large list.- getStateFlow supplies an initial empty value and emits saved changes through a read-only StateFlow. Updating SavedStateHandle updates the exposed query.
- The input callback is an event boundary. A stateless field needs only a String and a callback, making previews and UI tests independent of the ViewModel.
- Use the query to derive results from current data. If a selected ID no longer exists after reconstruction, show a not-found or cleared-selection state instead of crashing.
Student practice and expected behavior
Add a searchable fake directory. Log a nonpersonal ViewModel creation marker, save only its query, and compare recomposition, rotation and system process recreation on a test emulator.
- A normal input update changes the text without creating a new Activity or ViewModel.
- Rotation restores the UI and keeps the destination's ViewModel while its owner remains retained.
- For system process recreation: type a query, press Home, run adb shell am kill YOUR_TEST_PACKAGE, then return through Recents. Verify a new owner reconstructs the query when saved state is restored.
- Force-stop, clearing app data and dismissing a task are separate experiments; do not claim they are equivalent to system process recreation.
Write down the input, expected outcome and observed outcome for each check. Include a screenshot, a short recording or a nonpersonal diagnostic trace appropriate to the behavior you are proving.
Debugging clinic
- Developer option 'Don't keep activities' exercises Activity destruction, not the same failure as killing the process.
- A manually instantiated ViewModel can bypass its proper store/factory and saved-state setup.
- A saveable query does not make a session database durable. Keep persistent records and reconstruction inputs separate.
- A lifecycle-aware collector does not cancel every producer or background job automatically.
Instructor prompts
- Draw four nested lifetimes and ask students to place a query, a ViewModel, a coroutine and a saved session.
- Have learners predict which object is recreated before each emulator experiment; compare with creation markers.
- Ask what the app should show when a restored selected ID points to a deleted record.
Teach-back checkpoint
Quick check and references
Quick check
1. Which object automatically survives arbitrary system process death in memory?
2. What is a sensible SavedStateHandle value?
Continue with the phase capstone after demonstrating the practice checks. Use the official documentation to verify APIs against your project's selected dependency versions.