You need to run background work on Android - how do you choose between WorkManager, a foreground service, and AlarmManager?
WorkManager is the default for deferrable work that must eventually run, a foreground service is for work the user is actively aware of right now, and AlarmManager is only for work that must fire at a specific wall-clock time regardless of system state.
What the interviewer is scoring
- Does the candidate frame the choice around user-visibility and timing guarantees rather than "which API is newer"
- Can they explain why WorkManager survives process death and reboot while a plain coroutine scope does not
- Whether they know a foreground service requires an active, ongoing notification and why that constraint exists
- Do they mention Doze mode and app standby buckets as the reason naive background scheduling gets deferred
- Can they identify a case where none of the three is appropriate, such as work that must complete before the current screen proceeds
Answer
Short answer
WorkManager is the default for deferrable work that must eventually run, a foreground service is for work the user is actively aware of right now, and AlarmManager is only for work that must fire at a specific wall-clock time regardless of system state.
Keep android explicit in the answer because that is the concept the interviewer is actually trying to test. A good android explanation names the trade-off, the failure mode, and the evidence you would use before choosing.
The question underneath the API names
All three mechanisms answer a different question about the work, and picking between them by feature-comparing APIs misses that they are not really alternatives to each other in most cases - they are the correct answer to three different constraints. The question to ask first is: does the user need to be aware this is happening right now, does it merely need to eventually happen, or does it need to happen at a specific moment regardless of anything else. Those three answers point at a foreground service, WorkManager, and AlarmManager respectively, in that order of how common the actual need turns out to be.
WorkManager: deferrable work that must eventually complete
WorkManager is the right default for background work that has to run reliably but has no hard deadline the user is watching - uploading a photo taken offline, syncing local changes to a server, periodic data refresh, compressing a file after capture. Its defining property is that it persists the work request to a database, so the work survives process death and even a device reboot, and it automatically picks the underlying execution mechanism (a job scheduled with JobScheduler, or an alarm plus a broadcast on older API levels) appropriate to the device it is running on. A coroutine launched in an app-level scope does none of this: kill the process and the coroutine, and the record that the work was ever requested, both disappear.
val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.build()
// Enqueued work is persisted; it will run even if the app process
// is killed before the network becomes available.
WorkManager.getInstance(context).enqueue(uploadRequest)
The trade-off is timing honesty: WorkManager explicitly does not guarantee when the work runs, only that it will run when its constraints are satisfied and the system allows it, and the system's Doze mode and app standby buckets can defer it substantially for an app the user has not opened recently. Expedited work requests narrow that window for genuinely time-sensitive but still background tasks, but they are still subject to a system-wide quota and are not a guarantee of immediate execution - they are a stronger request, not a promise.
Foreground service: work the user must see happening
A foreground service exists for work that is actively running and that the user should be able to see and, usually, cancel - music playback, an ongoing navigation session, a large file download the user is watching progress on. The defining constraint is that a foreground service must post a persistent, ongoing notification within a few seconds of starting, and the OS enforces this: calling startForegroundService() and then failing to call startForeground() promptly causes the system to stop the service and, on recent Android versions, crash the app with an exception rather than silently degrade. That requirement is deliberate - a foreground service is a trade where the app gets protection from being killed for OOM in exchange for being honest with the user that it is consuming resources right now.
flowchart TD
A[startForegroundService called] --> B{startForeground called<br/>within the time limit?}
B -- Yes --> C[Ongoing notification shown,<br/>service protected from OOM kill]
B -- No --> D[System stops the service<br/>and the app crashes]Using a foreground service for work the user has no reason to be watching is a misuse candidates propose surprisingly often, usually to dodge Doze deferral - it works, but it means showing the user a permanent notification for something they never asked to be told about, which is exactly the visibility contract the mechanism exists to enforce.
AlarmManager: work tied to wall-clock time
AlarmManager is for the narrow case where the work genuinely must happen at a specific point in time regardless of what else is going on - an alarm clock app, a calendar reminder at an exact minute, a timed one-off action the user explicitly scheduled.
It is not a general background-work API and should not be reached for as a substitute for WorkManager's periodic scheduling, because exact alarms bypass the battery-saving deferral that Doze mode exists to enforce, and recent Android versions require a separate, user-visible permission to schedule them for that reason. Using setExactAndAllowWhileIdle for routine background sync is the kind of choice that gets an app flagged for excessive battery use, because it is asking the system to wake the device from a low-power state for something that had no actual deadline.
The case none of the three fits
If work must finish before the current screen can proceed - fetching data a screen cannot render without, validating a form submission - none of these three is the right tool, because all three are explicitly for work that continues independently of the current UI. That case wants a coroutine scoped to the screen's own lifecycle (viewModelScope or a LaunchedEffect), with a loading state the UI shows while it runs. Reaching for WorkManager here because "it's the modern background API" produces a UI that has to poll or observe a WorkInfo state just to know when work the user is directly waiting on has finished, which is more machinery than the problem needed.
Pick by what the work owes the user - a visible, cancellable ongoing task wants a foreground service, an eventual guarantee wants WorkManager, and an exact moment in time is the only real reason to reach for AlarmManager directly.
© 2026 Preptima. Originally published at preptima.com.
Likely follow-ups
- What happens to a running WorkManager job if the OS kills the app process partway through?
- Why does starting a foreground service without immediately calling startForeground() crash on modern Android?
- How does WorkManager's own internal use of AlarmManager and JobScheduler change your answer about when to use AlarmManager directly?
- What expedited work in WorkManager is for, and why isn't it a substitute for a foreground service?
Related questions
- How do you leverage WorkManager to guarantee execution, handle conflict resolution, and optimise battery usage in an offline-first Android application requiring bidirectional synchronisation?hardAlso on android and workmanager3 min
- An Android app queues actions while offline. After the process is killed and restarted, some actions sync twice and the user sees duplicate orders. How do you design the fix?hardAlso on workmanager4 min
- How do you choose which devices to test on, and what will an emulator never tell you?mediumAlso on android5 min
- A Compose screen with a long list is janky while scrolling - walk through how you'd find and fix the recomposition problem causing it.mediumAlso on android5 min