WorkManager for background tasks

Alex Chen Jan 2026
2 tabs
package com.example.myapp.workers

import android.content.Context
import androidx.hilt.work.HiltWorker
import androidx.work.*
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import java.util.concurrent.TimeUnit

@HiltWorker
class SyncWorker @AssistedInject constructor(
    @Assisted context: Context,
    @Assisted params: WorkerParameters,
    private val repository: PostRepository
) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        return try {
            // Get input data
            val forceRefresh = inputData.getBoolean(KEY_FORCE_REFRESH, false)

            setProgress(workDataOf(PROGRESS to 0))

            repository.syncPosts(forceRefresh)

            setProgress(workDataOf(PROGRESS to 100))

            // Return success with output data
            Result.success(
                workDataOf(
                    KEY_SYNC_TIME to System.currentTimeMillis()
                )
            )
        } catch (e: Exception) {
            if (runAttemptCount < 3) {
                Result.retry()
            } else {
                Result.failure(
                    workDataOf(KEY_ERROR to e.message)
                )
            }
        }
    }

    companion object {
        const val KEY_FORCE_REFRESH = "force_refresh"
        const val KEY_SYNC_TIME = "sync_time"
        const val KEY_ERROR = "error"
        const val PROGRESS = "progress"

        fun schedulePeriodicSync(context: Context) {
            val constraints = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .setRequiresBatteryNotLow(true)
                .build()

            val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(
                15, TimeUnit.MINUTES
            )
                .setConstraints(constraints)
                .setBackoffCriteria(
                    BackoffPolicy.EXPONENTIAL,
                    10, TimeUnit.SECONDS
                )
                .build()

            WorkManager.getInstance(context)
                .enqueueUniquePeriodicWork(
                    "sync_posts",
                    ExistingPeriodicWorkPolicy.KEEP,
                    syncRequest
                )
        }

        fun scheduleOneTimeSync(
            context: Context,
            forceRefresh: Boolean = false
        ) {
            val inputData = workDataOf(KEY_FORCE_REFRESH to forceRefresh)

            val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>()
                .setInputData(inputData)
                .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
                .build()

            WorkManager.getInstance(context)
                .enqueueUniqueWork(
                    "sync_posts_once",
                    ExistingWorkPolicy.REPLACE,
                    syncRequest
                )
        }
    }
}
2 files · kotlin Explain with highlit

WorkManager schedules deferrable, guaranteed background work. I create Worker or CoroutineWorker subclasses with doWork() method. Constraints specify when work runs—network type, charging status, battery level. One-time requests use OneTimeWorkRequest, periodic work uses PeriodicWorkRequest with minimum 15-minute intervals. Chaining enables dependent tasks with then() and combine(). Work survives app restarts and device reboots. Data objects pass parameters in/out. I observe work status with WorkInfo LiveData. Unique work ensures single instances with conflict policies. WorkManager handles scheduling across API levels, using JobScheduler, AlarmManager, or BroadcastReceiver based on device capabilities.