Every app lives on a rollercoaster of states: sometimes it is right in front of you receiving taps, sometimes it gets pushed to the back when a call comes in, and sometimes it is simply killed by the system to free up memory. Understanding this lifecycle is the first step toward writing apps that don't lose data or crash when the user switches screens. It is knowledge you use every day, and it comes up in the very first technical conversations.
The states of an app
Regardless of iOS, Android, React Native or Flutter, an app moves between a few fundamental states. The naming changes, but the idea is the same.
- Foreground (active): the app is visible and receiving touch events. This is where everything "happens".
- Background: the user left (pressed home, opened another app), but your app is still alive in memory. You have little time and limited resources here.
- Suspended (inactive): still in memory, but not executing code. A "frozen pause" state.
- Terminated: the process was destroyed. When the user returns, the app starts from scratch.
State diagram
[Not started]
|
v
[Foreground] <----+
| ^ |
(leave)| |(return) |
v | |
[Background] |
| |
(OS frees memory) |
v |
[Terminated] ------+ (starts from scratch)
What happens when the user leaves and comes back
Picture the app as a person reading a book. When someone interrupts them, they can put in a bookmark (save state) or just slam the book shut. If the operating system decides to "kill" the app in the background to give memory to another one, the user expects to pick up where they left off when they return — not start over.
The critical point: coming back from background (app alive) is different from coming back from terminated (app dead). In the first case your in-memory state still exists; in the second, it doesn't. Treating both as the same is a source of bugs.
Saving and restoring state
Each platform offers hooks so you can persist what you need before memory is lost: Android has onSaveInstanceState, iOS has state restoration, and frameworks like Flutter have their own equivalents. The rule is to save the minimum needed to rebuild the screen: typed text, scroll position, selected item.
// pseudo-code when going to background
onSaveState(bundle) {
bundle.put("draft", textField.value)
bundle.put("scrollY", list.position)
}
onRestore(bundle) {
textField.value = bundle.get("draft")
}
Why this matters: rotation and killed apps
On Android, rotating the screen recreates the entire screen by default — technically it is the same "die and be reborn" mechanism. If you didn't save state, the half-filled form vanishes. Interviewers often ask you to explain exactly what happens when the screen rotates and how to avoid losing data — the expected answer hinges on understanding this lifecycle.
Common bugs from ignoring the lifecycle
- Resource leaks: registering a listener or opening the camera in the foreground and forgetting to release it in the background.
- Zombie timers and requests: continuing to update the UI of a screen that is no longer visible.
- State lost on rotation: the classic form that resets when you turn the phone.
- Assuming the app never dies: keeping data only in memory and losing everything when the OS reclaims resources.
The operating system owns the memory, not your app. Program expecting to be interrupted and terminated at any moment — whoever assumes this writes robust apps.
Conclusion
The lifecycle defines when your code runs and when your data survives. Mastering the states (foreground, background, suspended, terminated), knowing the difference between returning alive and being reborn from scratch, and saving/restoring the essentials is the foundation everything else rests on — including navigation between screens.