At the heart of every operating system — mobile or otherwise — is a fundamental question: what is a process actually doing at any given moment, and how does the system decide what it should do next? This is formalized through the concept of process states and the transitions between them. In classical operating system theory, this concept dates back decades, but mobile operating systems like iOS and Android have adapted and extended it significantly to account for the unique constraints of battery-powered, memory-constrained, user-attention-driven devices. This article explains both the classical foundation and its mobile-specific evolution in depth.
Classical Process State Model (OS Theory Foundation)
In traditional operating systems theory, a process typically moves through some variation of the following states:
- New: The process is being created.
- Ready: The process is loaded into memory and waiting for CPU time, but not currently executing.
- Running: The process is actively executing instructions on the CPU.
- Waiting/Blocked: The process is waiting for some event (I/O completion, a resource becoming available, a signal) and cannot proceed until that event occurs.
- Terminated: The process has finished execution or been killed, and its resources are being reclaimed.
Transitions between these states are triggered by specific events: a scheduler dispatch moves a process from Ready to Running; an I/O request moves it from Running to Waiting; an I/O completion moves it from Waiting back to Ready; time-slice expiration moves it from Running back to Ready; and process completion or termination moves it to Terminated. This is the foundation taught in every operating systems course, and it underlies Linux, Windows, and macOS process scheduling — including, at a low level, the Linux kernel that both Android and (via XNU/Darwin lineage on the BSD side) iOS ultimately build upon.
Why Mobile Operating Systems Extend This Model
Classical process states answer “is this process executing right now,” but they don’t answer a mobile-specific question that matters enormously for user experience and battery life: “is this process visible and relevant to the user right now, and how urgently does it deserve system resources if it’s not?” Desktop operating systems generally don’t need a strong answer to this, since desktop resources are comparatively abundant and users expect background apps to keep running. Mobile operating systems, constrained by battery and RAM, needed a much richer state model layered on top of the classical one — one that reflects the user-facing lifecycle of an app, not just its low-level CPU scheduling state.
iOS App States (User-Lifecycle Layer)
As covered in more detail in a companion article on iOS background management, iOS defines these application-lifecycle states:
- Not Running: No process exists for the app.
- Inactive: In the foreground, but momentarily not receiving events (e.g., during a system interruption like an incoming call).
- Active: In the foreground, fully receiving events — the classical “Running” state, from the user’s perspective.
- Background: Not visible, but executing code under a specific permission (a background mode or task assertion).
- Suspended: In memory, but not executing any code at all — effectively frozen, and eligible for silent termination if the OS needs the memory.
Transitions
- Not Running → Inactive → Active: App launch, moving through a brief inactive transition state before becoming fully active.
- Active → Inactive: Triggered by an interruption (incoming call, Control Center being pulled down) or the beginning of a transition to the background.
- Inactive → Background: The user has switched away from the app (Home gesture, app switch); if the app has background work permission, it continues executing briefly or under a declared background mode.
- Background → Suspended: Once background work (if any) completes, or the OS decides the app’s background time allowance is up, the app is frozen in place.
- Suspended → Background/Active: If the user returns to the app, it’s instantly resumed (no relaunch needed) directly back to Active, generally passing back through Background/Inactive states briefly for lifecycle callback purposes.
- Suspended → Not Running: The OS can silently terminate a suspended app at any time under memory pressure, with no callback executed — this is why apps must persist state proactively rather than relying on in-memory state surviving indefinitely.
This state machine is exposed directly to developers through UIApplicationDelegate lifecycle callbacks (applicationDidBecomeActive, applicationWillResignActive, applicationDidEnterBackground, applicationWillEnterForeground, applicationWillTerminate), letting apps respond appropriately at each transition — e.g., pausing a game’s physics simulation on applicationWillResignActive, or saving critical data on applicationDidEnterBackground before a potential silent termination.
Android Process States (Priority-Based Layer)
Android’s model is conceptually similar in spirit but structured differently, built around process importance levels, which the system’s ActivityManager uses to decide which processes to keep alive and which to kill first under memory pressure. From highest to lowest priority:
- Foreground process: Directly interacting with the user (an Activity in the resumed state), running a foreground service with an active notification, or executing a
BroadcastReceiver‘sonReceive()method. These are the last processes Android will ever kill. - Visible process: Not directly in the foreground but still visible to the user in some way (e.g., an Activity visible but not focused, such as behind a dialog, or a process bound to a visible foreground service).
- Service process: Running a started background service that isn’t classified as foreground or visible — doing work like data sync — but with no direct user-visible presence.
- Background process: Holds an Activity that is not currently visible (the user has navigated away). Android keeps a list of these (an LRU cache) and can kill any of them at any time to reclaim memory, generally killing the least-recently-used first.
- Empty process: Holds no active application components at all, but is kept around purely as a cache to speed up the next launch of that app, since starting a process from scratch has real overhead. These are the first to be killed under memory pressure.
Transitions
Android’s transitions are driven by a combination of the Activity lifecycle (onCreate, onStart, onResume, onPause, onStop, onDestroy) at the component level, and process-level importance recalculation performed by ActivityManager whenever an app’s visible/foreground/background status changes. For example:
- Launching an app: process created → Activity moves through
onCreate → onStart → onResume, making the process a Foreground process. - Pressing Home: Activity moves through
onPause → onStop, and the process importance typically drops to Background (or Cached/Empty if no components remain active), making it eligible for termination if memory is needed. - System low on memory: the Low Memory Killer (or, in modern Android, more nuanced memory management components) selects victims starting from the lowest-importance (Empty, then Background) processes.
- Returning to a killed background app: Android transparently creates a new process and, if the app implemented
onSaveInstanceState/onRestoreInstanceStateproperly, restores the Activity to something resembling its prior state, similar in spirit to iOS’s state restoration expectations after silent termination.
Comparative Table: iOS vs. Android Process States
| Concept | iOS | Android |
|---|---|---|
| Foreground/active state | Active | Foreground process |
| Momentarily non-interactive foreground | Inactive | (Handled within Activity lifecycle, e.g., onPause) |
| Executing but not visible | Background | Service process / Foreground service |
| Frozen, resumable, no execution | Suspended | Background / Cached (Empty) process |
| Basis for kill decisions | Suspension order + system memory pressure | Explicit importance hierarchy (Foreground > Visible > Service > Background > Empty) |
| Developer lifecycle hooks | UIApplicationDelegate methods | Activity/Service lifecycle callbacks (onPause, onStop, onDestroy, etc.) |
Why This Matters in Practice
Understanding process states and transitions isn’t just theoretical — it directly determines what developers can rely on and what users should expect:
- State persistence discipline: Because both platforms can terminate a background/suspended/cached process without warning, well-designed apps must treat every backgrounding event as a possible last chance to persist important data — never assuming a graceful termination callback will always fire.
- Battery and thermal management: By tying execution privileges tightly to visibility and declared necessity (rather than allowing indefinite background execution as classical OS process models implicitly permit), mobile OS designers directly leverage the state model as a battery-conservation mechanism, not just a scheduling abstraction.
- Perceived responsiveness: Prioritizing the state of the foreground process above all others (true on both platforms) is what allows a phone with objectively less raw compute power than a desktop to still feel instantly responsive to touch input — the scheduler and memory manager are both actively protecting that one process’s resources above everything else.
Underlying Kernel-Level Reality
It’s worth noting that beneath these high-level, platform-specific state models, both iOS (via the XNU kernel, part of Darwin) and Android (via the Linux kernel) still implement the classical Ready/Running/Waiting/Terminated process (and thread) states at the kernel scheduling level. The mobile-specific states discussed above are a policy layer built on top of, not a replacement for, this classical foundation — user-lifecycle states like iOS’s “Suspended” or Android’s “Cached” primarily determine whether the OS keeps the process’s memory around and how eagerly it should be scheduled or reclaimed, while the kernel scheduler still handles the fine-grained Ready/Running/Waiting mechanics for whatever threads happen to be active within a process at any given moment.
Practical Example
Imagine using a navigation app: it’s Active/Foreground while you’re looking at the map. You switch to check a text message — the navigation app becomes Inactive briefly, then Background (continuing to execute, since it declared a location background mode), tracking your position and giving turn-by-turn audio cues even though it’s not visible. If you then open several other memory-heavy apps, the OS respects the navigation app’s active background location work and does not suspend or kill it, precisely because its importance/background-mode status keeps it prioritized above ordinary suspended/cached apps — a direct, practical illustration of how the state model governs real resource allocation decisions, not just bookkeeping.
Best Practices for Developers
- Persist critical state at every meaningful lifecycle transition, not just at explicit termination callbacks, since termination can be silent.
- On Android, avoid unnecessarily elevating your process’s importance (e.g., avoid unnecessary foreground services with persistent notifications), since it directly reduces the pool of memory available system-wide and can itself become a source of user complaints.
- On iOS, use the appropriate background mode precisely matching your use case rather than trying to maximize background execution time through workarounds, which Apple’s App Review process actively screens against.
- Test cold-start-after-termination flows explicitly (both platforms provide developer tools to simulate this) rather than only testing warm resume-from-suspend/cache flows, since real-world users experience both regularly.
Troubleshooting Tips
- App loses data after being backgrounded briefly: Usually indicates a missing or incorrect state-persistence implementation at the relevant lifecycle callback, not a bug in the OS itself.
- App seems to restart unexpectedly even with plenty of free RAM: On Android, check whether the app’s process importance dropped to Background/Cached due to no active components remaining, making it an eligible (if not urgent) termination target even under only moderate memory pressure.
- Background location/audio stops unexpectedly: Verify the correct background mode entitlement is declared and that no system-level restriction (e.g., iOS Low Power Mode, Android Battery Saver) is suppressing it.
FAQs
Q: Is Android’s process state model the same as its Activity lifecycle? A: They’re related but distinct — the Activity lifecycle governs a specific UI component’s states (onCreate, onResume, etc.), while process importance/state governs the entire process hosting potentially multiple components, and is what the system uses for memory-reclamation decisions.
Q: Can a suspended iOS app or a cached Android process still receive notifications? A: Yes — push notification delivery and display are handled by the OS/notification system independently of the app process’s execution state; the app process may be briefly woken only if it needs to fetch additional data before displaying the notification.
Q: Why do mobile OSes bother with more states than desktop OSes? A: Because mobile devices must optimize for battery life and limited RAM far more aggressively, the extra granularity (Suspended vs. Background vs. Active on iOS; the five-tier importance hierarchy on Android) allows much more precise, resource-aware decisions about what to keep running and what to reclaim.
Summary
Process states and transitions in mobile operating systems build on the classical Ready/Running/Waiting/Terminated model from OS theory but add a rich, user-lifecycle-aware policy layer on top: iOS’s Active/Inactive/Background/Suspended states and Android’s five-tier process importance hierarchy (Foreground, Visible, Service, Background, Empty). These mobile-specific state models exist specifically to let the OS make aggressive, battery- and memory-conscious decisions about which processes deserve CPU time and which are safe to freeze or terminate — all while still relying on the same fundamental kernel-level scheduling primitives that underpin every modern operating system.
References
- Apple Developer Documentation — Managing Your App’s Life Cycle: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
- Android Developers — Processes and Application Life Cycle: https://developer.android.com/guide/components/activities/process-lifecycle
- Android Developers — Activity Lifecycle: https://developer.android.com/guide/components/activities/activity-lifecycle
- Silberschatz, Galvin, Gagne — Operating System Concepts (process state model reference, publisher site): https://www.os-book.com/
