Android WorkManager Guide — Background Task Scheduling
WorkManager handles background tasks reliably across different Android versions, respecting doze mode and battery optimization.
CoroutineWorker Basics
class MyCoroutineWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
val result = performBackgroundTask()
Result.success()
} catch (e: Exception) {
when {
e is IOException -> Result.retry()
else -> Result.failure()
}
}
}
private suspend fun performBackgroundTask(): String {
delay(2000)
return "Task completed"
}
}
WorkRequest with Constraints
OneTimeWorkRequest:
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.setRequiresDeviceIdle(false)
.build()
val workRequest = OneTimeWorkRequestBuilder<MyCoroutineWorker>()
.setConstraints(constraints)
.setBackoffCriteria(
backoffPolicy = BackoffPolicy.EXPONENTIAL,
initialDelay = 15,
timeUnit = TimeUnit.MINUTES
)
.build()
WorkManager.getInstance(context).enqueueUniqueWork(
"sync_work",
ExistingWorkPolicy.KEEP,
workRequest
)
PeriodicWorkRequest
val periodicWorkRequest = PeriodicWorkRequestBuilder<MyCoroutineWorker>(
15, TimeUnit.MINUTES
).setConstraints(constraints).build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"periodic_sync",
ExistingPeriodicWorkPolicy.KEEP,
periodicWorkRequest
)
Input/Output Data
val inputData = workDataOf(
"user_id" to 123,
"action" to "sync"
)
val workRequest = OneTimeWorkRequestBuilder<MyCoroutineWorker>()
.setInputData(inputData)
.build()
// In worker
override suspend fun doWork(): Result {
val userId = inputData.getInt("user_id", -1)
val action = inputData.getString("action")
val outputData = workDataOf(
"result" to "success",
"timestamp" to System.currentTimeMillis()
)
return Result.success(outputData)
}
WorkRequest Chaining
Sequential Execution:
val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>().build()
val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>().build()
val notifyRequest = OneTimeWorkRequestBuilder<NotifyWorker>().build()
WorkManager.getInstance(context)
.beginWith(uploadRequest)
.then(syncRequest)
.then(notifyRequest)
.enqueue()
Parallel + Sequential:
val task1 = OneTimeWorkRequestBuilder<Task1Worker>().build()
val task2 = OneTimeWorkRequestBuilder<Task2Worker>().build()
val merge = OneTimeWorkRequestBuilder<MergeWorker>().build()
WorkManager.getInstance(context)
.beginWith(listOf(task1, task2)) // Run in parallel
.then(merge) // Then merge results
.enqueue()
Monitor Progress in Compose
@Composable
fun WorkProgressScreen() {
val workManager = WorkManager.getInstance(LocalContext.current)
val workState by workManager
.getWorkInfoByIdLiveData(workId)
.observeAsState()
Column(Modifier.fillMaxSize()) {
when {
workState?.state == WorkInfo.State.RUNNING -> {
val progress = workState?.progress?.getInt("progress", 0) ?: 0
LinearProgressIndicator(progress = progress / 100f)
Text("Progress: $progress%")
}
workState?.state == WorkInfo.State.SUCCEEDED -> {
Text("Task completed successfully")
}
workState?.state == WorkInfo.State.FAILED -> {
Text("Task failed")
}
}
}
}
Key Takeaways
- Use CoroutineWorker for suspending operations
- Set Constraints for optimal execution conditions
- Chain work with beginWith/then for complex flows
- Pass data with workDataOf (JSON serializable types)
- Monitor in UI with observeAsState and Compose
- Implement exponential backoff for network retries
8 Android app templates: Gumroad
Top comments (0)