Background Work

Android Development โ€” Production & Publishing
Course 3 ยท Chapter 3 ยท Background Work

๐Ÿ”ง Background Work

Every coroutine so far in this course (viewModelScope, lifecycleScope โ€” Course 2, Chapter 6) is tied to a screen or ViewModel being alive. Some work genuinely needs to happen even after the app is closed โ€” syncing data, uploading a file, sending a scheduled reminder. Android aggressively kills background processes to save battery, so this kind of work needs its own dedicated system: WorkManager.

๐Ÿค” 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

class SyncWorker( context: Context, params: WorkerParameters ) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { return try { // The actual background work โ€” e.g. calling a repository's refresh() repository.refresh() Result.success() } catch (e: IOException) { Result.retry() // WorkManager will reschedule this automatically } } }

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

// A one-off task โ€” runs once, as soon as constraints allow val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>().build() WorkManager.getInstance(context).enqueue(syncRequest) // A recurring task โ€” repeats on an interval (minimum 15 minutes) val periodicSync = PeriodicWorkRequestBuilder<SyncWorker>(1, TimeUnit.HOURS).build() WorkManager.getInstance(context).enqueue(periodicSync)

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

val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .setRequiresCharging(true) .build() val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>() .setConstraints(constraints) .build()

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

WorkManagerForeground Service
User awarenessInvisible, deferrableVisible, persistent notification required
Timing"Whenever constraints allow"Immediately, continuously, until stopped
Typical useSync, uploads, periodic maintenanceMusic playback, navigation, active downloads
Survives process death?Yes, automatically reschedulesNo โ€” the service itself is what's running
โš  Choosing the Wrong One Is a Common Mistake

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 RedisYes โ€” WorkManager persists its own queue
Retry on failureConfigurable retry/backoffResult.retry() + WorkManager's backoff policy
SchedulingDelayed jobs, repeatable jobs (cron-like)OneTimeWorkRequest / PeriodicWorkRequest
ConstraintsApplication-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.

โ†’ Solution

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.

โ†’ Solution

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.

โ†’ Solution

๐Ÿ’ก WorkManager Is the Bridge to "Real App" Concerns

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.