DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Android WorkManager Guide — Background Task Scheduling

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"
    }
}
Enter fullscreen mode Exit fullscreen mode

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
)
Enter fullscreen mode Exit fullscreen mode

PeriodicWorkRequest

val periodicWorkRequest = PeriodicWorkRequestBuilder<MyCoroutineWorker>(
    15, TimeUnit.MINUTES
).setConstraints(constraints).build()

WorkManager.getInstance(context).enqueueUniquePeriodicWork(
    "periodic_sync",
    ExistingPeriodicWorkPolicy.KEEP,
    periodicWorkRequest
)
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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")
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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)