Designing phone apps for interruptions
On a phone, being interrupted is the normal case. A call comes in, a notification pulls the user away, the screen locks, the device rotates, the battery runs low, or Android quietly kills your app in the background. Good mobile apps expect all of this and put the user back exactly where they were.
Key takeaways
- Assume every session can end at any moment, and save work as it happens.
- Use
rememberSaveableandSavedStateHandlefor UI state that must survive process death. - Make it obvious where the user left off: keep their position, selection and partly filled fields.
- Respect the battery on all-day devices: no polling, no wake locks, work batched with WorkManager.
Mobile interruptions are the normal case
People use phones in short bursts between other things. A count gets paused to answer a customer, a checklist gets abandoned for a phone call, an inspection gets finished an hour later in the car. Every one of those moments is a chance for the app to lose work or lose the user's place.
Desktop habits make this worse: modal flows that only save at the end, wizards that start over if you leave, and forms that clear themselves when the app goes to the background. On a portable device, design every screen as if it could be left halfway at any second.
| Interruption | What Android does | What your app must do |
|---|---|---|
| Phone call, notification, app switch | Your Activity is stopped but usually kept in memory. | Nothing special, as long as state is saved as it changes. |
| Screen rotation, dark mode, language change | A configuration change; by default the Activity is recreated. | Keep state in a ViewModel or rememberSaveable. |
| Long time in the background, low memory | The whole process can be killed and later restored. | Restore UI state from saved instance state and data from storage. |
| Swipe away from recents, force stop, reboot | The app starts fresh; saved instance state is discarded. | Persist important data to disk, not just to saved state. |
Process death: the interruption you never see
When memory is tight, Android can kill a backgrounded app's process. When the user comes back from the recents screen, Android recreates the Activity and hands it a saved instance state bundle. If you only kept state in plain variables, the screen comes back empty, even though the user never closed the app. This is called Android process death, and it is one of the most common causes of "the app lost my work" complaints.
You can reproduce it on an emulator or a phone with USB debugging:
adb shell am kill com.example.thumbinventory
Put the app in the background first (press Home), run the command, then reopen it from recents. Whatever survives is what your users will see.
Save UI state with rememberSaveable and SavedStateHandle
In Jetpack Compose, remember keeps a value across recompositions but loses it on configuration changes and process death. rememberSaveable stores the value in the saved instance state bundle, so it comes back after both:
var selectedZone by rememberSaveable { mutableStateOf(Zone.FRIDGE) }
var searchText by rememberSaveable { mutableStateOf("") }
For state that lives in a ViewModel, use SavedStateHandle. It is a key-value map that is written into the same bundle:
class InspectionViewModel(private val savedState: SavedStateHandle) : ViewModel() {
val currentStep = savedState.getStateFlow("current_step", 0)
fun goToStep(step: Int) {
savedState["current_step"] = step
}
}
Saved instance state is for small UI values: a selected tab, a scroll position, a half-typed field. Keep it well under the bundle size limit and store real data, like records and photos, on disk.
Rotation and other configuration changes
Rotating the phone, folding or unfolding a foldable, switching to dark mode or changing the system language are configuration changes. By default Android destroys and recreates the Activity so it can load resources for the new configuration.
The manifest that android_scaffold generates declares android:configChanges="orientation|screenSize|screenLayout|keyboardHidden", so rotation and window resizing don't recreate the Activity; Compose simply recomposes with the new size. Other changes, such as dark mode or locale, still recreate it, and process death still happens. Treat configChanges as a smoothness improvement, not as a replacement for saving state.
Resume exactly where the user left off
Coming back after an interruption should feel like turning back to a page in a book. Restore not only the data but the context:
- The same screen, tab or zone that was open.
- The same scroll position in long lists (
rememberLazyListState()andrememberLazyGridState()are already saveable). - Partly filled fields, including text that hasn't been submitted yet.
- A visible marker of progress, such as "Step 4 of 9" or "12 of 30 shelves counted".
Short, single-tap actions help too. In Thumb Inventory, each tap is a complete, saved change, so there is no half-finished state to lose at all. Designing actions this way is the cheapest interruption-proofing there is.
Do
- Save each answer in a long form as soon as it's entered.
- Keep drafts and show them on return.
- Make each action complete in one step where you can.
Avoid
- Multi-screen wizards that only save on the last screen.
- Timers that reset progress when the app is backgrounded.
- Restarting at a splash or home screen after process death.
Battery-friendly apps for all-day devices
A field worker's phone or rugged handheld often has to last a full shift. A dead battery is the ultimate interruption. Some habits that keep a small app light:
- Don't poll. Avoid loops that check a server or a sensor every few seconds. Use callbacks, and let the system wake you.
- Batch background work with
WorkManagerand constraints such as "network connected" or "charging", instead of running your own services. - Avoid keeping the screen on unless the task truly needs it, and clear the flag as soon as it's done.
- Prefer dark surfaces for apps that stay on screen for long periods; on OLED phones, dark pixels use less power.
- Release the camera, GPS and Wi-Fi sockets as soon as you stop using them, for example when the screen goes to the background.
A checklist for testing interruptions
- Start a task, press Home, open another app, come back. Nothing should change.
- Rotate the phone mid-task, then switch between light and dark mode.
- Background the app and run
adb shell am killwith your package name, then reopen from recents. - Swipe the app away from recents and reopen it. Saved data should be there, even if the screen starts fresh.
- Turn on battery saver and repeat the core task.
With --watch running, you can fix a lost-state bug, save and repeat these steps on the emulator within seconds. Pair this with the offline-first checklist for apps used out in the field.
Frequently asked questions
What is process death on Android?
Process death is when Android kills a backgrounded app's process to free memory. When the user returns, the Activity is recreated with its saved instance state, so any UI state that was not saved with rememberSaveable or SavedStateHandle is lost.
What is the difference between remember and rememberSaveable in Compose?
remember keeps a value across recompositions only. rememberSaveable also stores it in the saved instance state bundle, so it survives configuration changes such as rotation and process death.
How do I test my app for process death?
Put the app in the background, run adb shell am kill followed by your package name, then reopen the app from the recents screen. Alternatively, enable "Don't keep activities" in Developer options.
Does android_scaffold handle screen rotation?
The manifest it generates declares configChanges for orientation and screen size, so rotation does not recreate the Activity. You should still save important state, because process death and other configuration changes still recreate it.