Background Task Selection Guide - WorkManager vs Service vs AlarmManager
Choosing the right background task mechanism is crucial for Android app performance and battery life. Compare WorkManager, Service, and AlarmManager.
Task Type Selection Criteria
| Task Type | Recommended | Why |
|---|---|---|
| Deferrable background work | WorkManager | Respects device constraints, battery-friendly |
| Network requests, uploads | WorkManager + Foreground Service | Guaranteed to run, user-aware |
| Periodic tasks | WorkManager.PeriodicWorkRequest | Built-in retry, flexible scheduling |
| Scheduled alarms | AlarmManager | Exact/inexact timing, battery efficient |
| Long-running operations | Foreground Service | Ongoing notification, won't be killed |
| Real-time operations | Service | Low-level control, runs in background |
WorkManager Best Practices
// One-time work
val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setInputData(workDataOf("fileUri" to uri.toString()))
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
Duration.ofSeconds(15)
)
.addTag("upload")
.build()
WorkManager.getInstance(context).enqueueUniqueWork(
"file_upload",
ExistingWorkPolicy.KEEP,
uploadRequest
)
// Periodic work
val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(
Duration.ofHours(1)
).build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"sync",
ExistingPeriodicWorkPolicy.KEEP,
syncRequest
)
// Chained work
WorkManager.getInstance(context)
.beginWith(downloadRequest)
.then(processRequest)
.then(uploadRequest)
.enqueue()
Foreground Service
For user-visible operations:
class DownloadService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = createNotification()
startForeground(NOTIFICATION_ID, notification)
lifecycleScope.launch {
downloadFile()
}
return START_STICKY
}
private fun createNotification(): Notification {
val channel = NotificationChannel(
"download",
"Downloads",
NotificationManager.IMPORTANCE_LOW
)
return NotificationCompat.Builder(this, "download")
.setContentTitle("Downloading...")
.setProgress(100, 50, false)
.setSmallIcon(R.drawable.ic_download)
.build()
}
override fun onBind(intent: Intent?) = null
}
// Start from activity
val intent = Intent(context, DownloadService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
AlarmManager Scheduling
Exact and inexact alarms:
val alarmManager = context.getSystemService<AlarmManager>()
val intent = Intent(context, AlarmReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
// Inexact alarm (battery-friendly)
alarmManager?.setAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + 60000,
pendingIntent
)
// Exact alarm (requires permission)
alarmManager?.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + 60000,
pendingIntent
)
// Repeating alarm
alarmManager?.setInexactRepeating(
AlarmManager.RTC_WAKEUP,
System.currentTimeMillis(),
AlarmManager.INTERVAL_DAY,
pendingIntent
)
Comparison Table
| Feature | WorkManager | Service | AlarmManager |
|---|---|---|---|
| Battery efficiency | High | Medium | High |
| Guaranteed execution | Yes | No | Depends |
| Device reboots | Survives | No | Yes |
| User visibility | Optional | No | No |
| Simplicity | High | Low | Medium |
| Min interval | 15 mins | Real-time | Flexible |
Choose the right tool for each task to optimize battery life while meeting feature requirements.
8 Android app templates on Gumroad
Top comments (0)