Using Android WorkManager for Reliable Background Sync
Learn how Android WorkManager provides reliable, constraint‑aware background work that survives app restarts and reboots, with a concrete nightly sync example and practical verification steps.
04 Apr 2026, 14:49 UTC

Problem: Keeping data fresh when the app isn’t running
Many apps need to sync data—such as user preferences or remote configurations—on a regular basis, but the app may be killed or the device rebooted. Relying on AlarmManager or a foreground service adds complexity and can waste battery.
Thesis: WorkManager gives OS‑backed guarantees for deferrable work
WorkManager chooses the best available background mechanism (JobScheduler on API 23+, AlarmManager on older devices) and respects system‑imposed constraints, so tasks survive app restarts and reboots without requiring special permissions.
How WorkManager works
Worker
Define a class that extends androidx.work.Worker and implement doWork(). Return Result.success(), Result.failure() or Result.retry() to tell the system how to proceed.
WorkRequest
Wrap the Worker in a OneTimeWorkRequest or PeriodicWorkRequest. Attach constraints such as required network type or battery level.
Constraints & Uniqueness
Constraints are evaluated before execution; if they are not met the work is deferred. You can also enforce uniqueness by name or tag to avoid duplicate runs.
Worked example: nightly preference sync
class PrefSyncWorker(appContext: Context, params: WorkerParameters)
: Worker(appContext, params) {
override fun doWork(): Result {
// Replace with actual sync logic
val success = syncPreferencesFromServer()
return if (success) Result.success() else Result.failure()
}
}
// Build the request
val syncRequest = PeriodicWorkRequestBuilder(12, TimeUnit.HOURS)
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.setKeepResultForMillis(TimeUnit.DAYS.toMillis(1))
.addTag("pref_sync")
.build()
// Enqueue uniquely so only one instance exists
WorkManager.getInstance(context)
.enqueueUniqueWork(
"pref_sync_unique",
ExistingWorkPolicy.KEEP,
syncRequest
)
Place the code in your Kotlin source folder. The worker runs only when the device has a network connection and the battery is not low, and the system will retry with exponential backoff if doWork() throws.
Trade-offs and limitations
- WorkManager does not guarantee a specific execution window; under Doze or App Standby the task may be delayed until the next maintenance window.
- Starting with Android 12, certain background work may need a foreground service notification if it exceeds short‑running limits; however, pure deferrable work that respects constraints usually stays exempt.
- Blocking
doWork()for more than a few seconds can trigger an ANR or be killed by the OOM killer; keep the work off the main thread or useCoroutineWorkerfor suspendable logic.
Actionable closing
- Add the WorkManager dependency:
implementation "androidx.work:work-runtime-ktx:2.9.0"(check for latest). - Create your Worker class as shown above.
- Build and enqueue the request from a suitable place (e.g., Application.onCreate or after user login).
- Verify scheduling with
adb shell dumpsys job | grep pref_syncoradb shell cmd jobscheduler. - Check logs:
adb logcat | grep WorkManagerforSchedulerInfoand constraint messages.
With these steps you gain a battery‑friendly, survivable background sync that adapts to the device’s power state.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.