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
)
}
}
}
package com.example.myapp.workers
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.work.*
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import javax.inject.Inject
@HiltViewModel
class SyncViewModel @Inject constructor(
private val workManager: WorkManager
) : ViewModel() {
val syncStatus: StateFlow<SyncStatus> =
workManager.getWorkInfosForUniqueWorkFlow("sync_posts")
.map { workInfos ->
workInfos.firstOrNull()?.let { workInfo ->
when (workInfo.state) {
WorkInfo.State.RUNNING -> {
val progress = workInfo.progress.getInt(SyncWorker.PROGRESS, 0)
SyncStatus.Running(progress)
}
WorkInfo.State.SUCCEEDED -> {
val syncTime = workInfo.outputData.getLong(
SyncWorker.KEY_SYNC_TIME, 0
)
SyncStatus.Success(syncTime)
}
WorkInfo.State.FAILED -> {
val error = workInfo.outputData.getString(SyncWorker.KEY_ERROR)
SyncStatus.Failed(error)
}
else -> SyncStatus.Idle
}
} ?: SyncStatus.Idle
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = SyncStatus.Idle
)
fun triggerSync(forceRefresh: Boolean = false) {
SyncWorker.scheduleOneTimeSync(
workManager.configuration.executor as Context,
forceRefresh
)
}
fun cancelSync() {
workManager.cancelUniqueWork("sync_posts")
}
}
sealed class SyncStatus {
object Idle : SyncStatus()
data class Running(val progress: Int) : SyncStatus()
data class Success(val timestamp: Long) : SyncStatus()
data class Failed(val error: String?) : SyncStatus()
}
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.