A small Android learning app that shows where form state actually lives, and which of those stores survive:
- Recomposition
- Screen rotation
- Process death (task restored from Recents)
- A full restart after Force Stop
You fill in Name, Email, and Notes, then try to kill the UI in different ways. The State Inspector on the screen tells you whether the values came from the ViewModel, SavedStateHandle, or DataStore.
This is not a production form. It is a lab for Android lifecycle and state.
Android can throw away your UI without the user meaning to:
- Compose recomposes (redraws) constantly.
- Rotation destroys and recreates the Activity.
- The system can kill the process to reclaim memory while the task stays in Recents.
- The user can Force Stop or start a new task (Run from Android Studio, swipe away from Recents).
Those are different events. They need different stores.
| Store | Used for | Survives | Does not survive |
|---|---|---|---|
remember |
UI chrome only (section open/closed) | Recomposition | Rotation, process death |
ViewModel + StateFlow |
Live form on screen | Recomposition, rotation | Process death |
SavedStateHandle |
Unsaved draft | Process death if you return from Recents | Force Stop, Run, swipe-away Recents |
| DataStore | Permanent save | Force Stop, new launch, reboot | Nothing, until the user taps Save Permanently |
Two ideas the app keeps separate on purpose:
- Unsaved draft — every keystroke goes to the ViewModel and
SavedStateHandle. Not to disk. - Permanent form — written only when you tap Save Permanently. That is DataStore.
Clear Form clears the draft only. DataStore still has the last permanent save. Close the app fully and reopen: the saved form comes back.
Simulate Process Death cannot kill the process the way Android does. It flushes the draft and points you at Test 3. Use Logcat Terminate Application or adb shell am kill for a real kill.
FormScreen (Compose, stateless)
↓ events up, FormUiState down
FormViewModel (StateFlow + SavedStateHandle)
↓ only on Save Permanently / launch restore
FormRepository
↓
FormDataStore (Preferences DataStore)
The screen never talks to DataStore. The ViewModel never talks to the file API directly.
Build and run the app module on a device or emulator (com.processdeath.app). After each test, read the State Inspector at the bottom of the screen.
The same steps are inside the app under How to Test.
Goal: Prove the form does not live in the composition.
- Type a name.
- Expand or collapse How It Works / How to Test (that redraws UI).
- The form text should still be there.
Why it survives: The form is in the ViewModel StateFlow, outside Compose. Redrawing the UI does not recreate the ViewModel.
remember would also survive recomposition, but this app does not store Name / Email / Notes there. Those flags are only “is this section open?”
Goal: Prove ViewModel outlives the Activity.
- Enter data.
- Rotate the device (or enable rotation in the emulator).
- The form should still be there.
- How It Works / How to Test will likely close.
Why the form survives: The Activity is destroyed and recreated. The ViewModelStore is kept, so the new Activity gets the same ViewModel.
Why the sections close: Their expand state is remember. That dies with the composition.
Goal: Prove SavedStateHandle can restore a draft after the process is gone.
- Enter data. Do not tap Save Permanently.
- Press Home (leave the task in Recents).
- Kill the process without finishing the task:
- Android Studio Logcat → Terminate Application, or
adb shell am kill com.processdeath.app
- Open the app from Recents, not Run, not the launcher if that starts a new task.
Verify: Fields come back. Inspector says this session loaded from SavedStateHandle.
Limits of SavedStateHandle:
- Small data only (strings, ints, parcels). Not a database.
- Restored only if Android still has the task.
- Force Stop, Run from Android Studio, and swipe away from Recents start a new task. The draft is gone.
Do not use this button as a real kill. An app cannot reliably simulate system process death by calling killProcess on itself.
Goal: Prove DataStore is the “I meant to keep this” path.
- Enter data.
- Tap Save Permanently. You should see the saved message.
- Force Stop the app (Settings → Apps → Process Death Lab → Force Stop), or press Run again in Android Studio.
- Open the app from the launcher.
Verify: Fields come back. Inspector says this session loaded from DataStore.
Why DataStore: You asked to persist the form. That belongs on disk, not in a task-scoped bundle. Preferences DataStore is a file API for a few key-value fields. Room would be for a table of rows.
| Action | Skip Save, then kill | Save, then Force Stop |
|---|---|---|
Recents restore after am kill |
Draft from SavedStateHandle |
Same values, but source was disk if it was a new task |
| Force Stop / Run / new task | Empty, or last permanent save if you had one | Form from DataStore |
com.processdeath.app
├── MainActivity.kt Hosts ViewModel, collects StateFlow
├── ui
│ ├── FormScreen.kt Stateless UI + inspector + test guide
│ ├── FormViewModel.kt Draft + restore rules
│ └── FormUiState.kt Immutable snapshot
└── data
├── FormRepository.kt ViewModel → disk boundary
└── FormDataStore.kt Preferences DataStore
- Android Studio with the project SDK
minSdk24- Kotlin + Jetpack Compose + Material 3