WorkManager & background work
WorkManager is Jetpack's solution for deferrable, guaranteed background work: the work runs even if the app closes or the device restarts (tasks persist in a DB).
Core concepts:
- Worker / CoroutineWorker — the work itself:
doWork()returnsResult.success() / failure() / retry(). - WorkRequest — OneTimeWorkRequest / PeriodicWorkRequest (minimum interval 15 minutes).
- Constraints — network available, battery not low, charging, etc.
- Backoff policy — LINEAR/EXPONENTIAL delays between retries.
- Unique work —
enqueueUniqueWork(name, ExistingWorkPolicy.KEEP/REPLACE/APPEND)— deduplication. - Chaining —
beginWith(A).then(B).enqueue()— sequential/parallel chains.
When not WorkManager: work needed immediately (coroutines), work continuing while the user watches (foreground service), server-triggered work (FCM push). For exact-time execution, AlarmManager (setExactAndAllowWhileIdle).
class UploadAuditLogsWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result = try {
val logs = logStore.pending()
api.uploadLogs(logs)
logStore.markUploaded(logs)
Result.success()
} catch (e: IOException) {
if (runAttemptCount < 5) Result.retry() else Result.failure()
}
}
val request = OneTimeWorkRequestBuilder<UploadAuditLogsWorker>()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
.build()
WorkManager.getInstance(context).enqueueUniqueWork(
"upload_audit_logs",
ExistingWorkPolicy.KEEP, // artıq növbədədirsə, təzəsini əlavə etmə
request
)Guaranteed log upload: constraints + retry + uniqueness
The classic comparison question — "how would you do sync?": immediate + user waiting → coroutine + repository; deferrable + guaranteed (logs, analytics, backup) → WorkManager; exact time (a payment reminder) → AlarmManager; the server knows when (a new transaction) → FCM push handing off to WorkManager. Know Doze mode too: on a sleeping device WorkManager runs in maintenance windows — no timing guarantees.
🛠 Practice task
Write a CoroutineWorker: a network constraint (NetworkType.CONNECTED) + exponential backoff + enqueueUniqueWork(KEEP).
- Enqueue it in airplane mode, fully close the app, then re-enable the network — log that the work runs on its own.
- Return
Result.retry()and watchrunAttemptCountclimb. - Tap a sync button 5 times and confirm via
WorkManager.getWorkInfosthatKEEPleft exactly one job.
Done when: you have a decision tree ready for "WorkManager, foreground service or AlarmManager?".
📚 Sources and documentation
- WorkManagerofficialdeveloper.android.com
- Background work overviewofficialdeveloper.android.com