Background Work
๐ง Background Work
๐ค Why This Needs a Dedicated System
Unlike a server process or a browser tab, an Android app can be killed by the OS at almost any time it's not visible โ to reclaim memory, save battery, or because the user swiped it away. A plain viewModelScope.launch { } for something like "sync data every hour" would simply stop working the moment its owning ViewModel is gone, which could be seconds after the user leaves the screen. WorkManager exists specifically to survive all of that โ process death, even a device reboot for persisted work โ while still respecting the OS's own battery-saving constraints.
๐ ๏ธ Defining a Worker
doWork() is a suspend function (Kotlin Intermediate Chapter 1) โ the same coroutine fundamentals from every earlier chapter apply directly here. Result.success(), Result.failure(), and Result.retry() tell WorkManager what happened; retry() specifically triggers WorkManager's own backoff-and-reschedule logic, without any manual retry loop written by hand.
๐ค Enqueueing Work
OneTimeWorkRequest
Runs exactly once. The right choice for "upload this file now" or "sync after this specific user action" โ a discrete, single task.
PeriodicWorkRequest
Repeats on an interval, with a 15-minute floor enforced by Android itself (battery protection, not a WorkManager limitation) โ right for "check for updates every hour," wrong for anything needing tighter timing.
๐ Constraints โ Only Run When It Makes Sense
WorkManager delays running the work until every constraint is satisfied โ a sync task requiring a network connection simply won't attempt to run while the device is offline, rather than running and failing repeatedly. This declarative approach (state the requirements, let the system decide timing) mirrors this course's other declarative patterns โ the nav graph (Course 1, Chapter 7) describing destinations rather than imperative navigation calls, Room's @Query describing what data is needed rather than how to fetch it.
๐ Foreground Services โ For Work the User Should See
WorkManager is for deferrable work โ the user doesn't need to watch it happen in real time. Some work is the opposite: music playback, an active file download with a visible progress bar, a fitness-tracking session โ work that should run immediately and stay visibly running via a persistent notification. That's a foreground service, a different tool for a genuinely different job:
WorkManager vs Foreground Service
| WorkManager | Foreground Service | |
|---|---|---|
| User awareness | Invisible, deferrable | Visible, persistent notification required |
| Timing | "Whenever constraints allow" | Immediately, continuously, until stopped |
| Typical use | Sync, uploads, periodic maintenance | Music playback, navigation, active downloads |
| Survives process death? | Yes, automatically reschedules | No โ the service itself is what's running |
Reaching for a foreground service for something that's genuinely deferrable (like a periodic sync) forces an always-visible notification the user didn't ask for, and burns more battery than necessary. Reaching for WorkManager for something the user is actively watching (a download's progress bar) means the work could be delayed by constraints or battery optimization exactly when the user expects it to run immediately. Match the tool to whether the work is "the user is watching this right now" or "this can happen whenever."
WorkManager vs Node.js Background Jobs
| Node.js (BullMQ, Node Course 3) | Android WorkManager | |
|---|---|---|
| Survives the process restarting? | Yes โ jobs persist in Redis | Yes โ WorkManager persists its own queue |
| Retry on failure | Configurable retry/backoff | Result.retry() + WorkManager's backoff policy |
| Scheduling | Delayed jobs, repeatable jobs (cron-like) | OneTimeWorkRequest / PeriodicWorkRequest |
| Constraints | Application-level (custom logic) | Built-in (network, charging, battery, storage) |
๐ป Coding Challenges
Challenge 1: A One-Time Sync Worker
Write a LogSyncWorker (CoroutineWorker) whose doWork() logs a message and returns Result.success(), and enqueue it as a OneTimeWorkRequest from a Button's click handler in a composable.
Goal: Practice the basic Worker + OneTimeWorkRequest + enqueue flow.
Challenge 2: A Constrained Periodic Worker
Write a PeriodicWorkRequest for Challenge 1's worker (or a new one) that repeats every 6 hours, with constraints requiring a connected network and NOT requiring the device to be charging. Add a comment explaining what happens if the network constraint isn't met when the 6-hour interval elapses.
Goal: Practice PeriodicWorkRequest with constraints, and reason about constraint-not-met behavior.
Challenge 3: WorkManager vs Foreground Service
For each scenario, state which tool is appropriate and why: (a) backing up notes to the cloud once a day, (b) playing a podcast episode while the app is backgrounded, (c) retrying a failed image upload with exponential backoff, (d) showing live GPS navigation directions.
Goal: Practice the judgment call between the two tools based on the chapter's "is the user watching this right now" heuristic.
Everything through Course 2 assumed the app was open and visible. This chapter is the first genuine step into what happens when it isn't โ a distinction that barely exists for a typical web app (a closed tab just... stops) but is a constant, deliberate design consideration on mobile. The next few chapters (permissions, security, performance) continue in this same "production app" direction.
๐ฏ What's Next
Next chapter: Notifications & Permissions โ notification channels, runtime permissions, and handling denials gracefully.