WorkManager trong Android
1. Giới thiệu
WorkManager xử lý background tasks cần đảm bảo thực thi, ngay cả khi app bị tắt.
2. Khi nào dùng WorkManager?
| Use Case | Solution |
|---|---|
| Sync data định kỳ | WorkManager |
| Upload logs | WorkManager |
| Download files | WorkManager |
| Immediate user task | Coroutines |
| Foreground task | Foreground Service |
3. Setup
dependencies {
implementation("androidx.work:work-runtime-ktx:2.9.0")
}4. Tạo Worker
class UploadWorker(
context: Context,
workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result {
return try {
val imageUri = inputData.getString("image_uri") ?: return Result.failure()
// Upload logic
uploadImage(imageUri)
// Return success with output
val outputData = workDataOf("upload_url" to "https://...")
Result.success(outputData)
} catch (e: Exception) {
if (runAttemptCount < 3) {
Result.retry()
} else {
Result.failure()
}
}
}
}5. Schedule Work
One-time Work
val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setInputData(workDataOf("image_uri" to uri.toString()))
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
)
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
30, TimeUnit.SECONDS
)
.addTag("upload")
.build()
WorkManager.getInstance(context).enqueue(uploadRequest)Periodic Work
val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(
15, TimeUnit.MINUTES // Minimum 15 minutes
)
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.build()
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork(
"sync_work",
ExistingPeriodicWorkPolicy.KEEP,
syncRequest
)6. Constraints
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // Cần network
.setRequiredNetworkType(NetworkType.UNMETERED) // Cần WiFi
.setRequiresBatteryNotLow(true) // Battery > 20%
.setRequiresCharging(true) // Đang sạc
.setRequiresDeviceIdle(true) // Device idle
.setRequiresStorageNotLow(true) // Storage ok
.build()7. Work Chaining
WorkManager.getInstance(context)
.beginWith(downloadRequest)
.then(processRequest)
.then(uploadRequest)
.enqueue()
// Parallel then sequential
WorkManager.getInstance(context)
.beginWith(listOf(download1, download2, download3))
.then(mergeRequest)
.then(uploadRequest)
.enqueue()8. Observe Work Status
WorkManager.getInstance(context)
.getWorkInfoByIdLiveData(uploadRequest.id)
.observe(lifecycleOwner) { workInfo ->
when (workInfo?.state) {
WorkInfo.State.ENQUEUED -> showPending()
WorkInfo.State.RUNNING -> showProgress()
WorkInfo.State.SUCCEEDED -> {
val url = workInfo.outputData.getString("upload_url")
showSuccess(url)
}
WorkInfo.State.FAILED -> showError()
WorkInfo.State.CANCELLED -> showCancelled()
else -> {}
}
}
// With Flow
WorkManager.getInstance(context)
.getWorkInfoByIdFlow(uploadRequest.id)
.collect { workInfo ->
// Handle state
}9. Cancel Work
// By ID
WorkManager.getInstance(context).cancelWorkById(uploadRequest.id)
// By tag
WorkManager.getInstance(context).cancelAllWorkByTag("upload")
// By unique name
WorkManager.getInstance(context).cancelUniqueWork("sync_work")
// All
WorkManager.getInstance(context).cancelAllWork()10. Progress Updates
class DownloadWorker(...) : CoroutineWorker(...) {
override suspend fun doWork(): Result {
val files = inputData.getStringArray("files") ?: return Result.failure()
files.forEachIndexed { index, file ->
downloadFile(file)
// Update progress
setProgress(workDataOf("progress" to (index + 1) * 100 / files.size))
}
return Result.success()
}
}
// Observe progress
workManager.getWorkInfoByIdFlow(request.id).collect { info ->
val progress = info?.progress?.getInt("progress", 0) ?: 0
updateProgressBar(progress)
}11. Expedited Work (Android 12+)
val urgentRequest = OneTimeWorkRequestBuilder<UrgentWorker>()
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build()12. Hilt Integration
@HiltWorker
class SyncWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted workerParams: WorkerParameters,
private val repository: DataRepository
) : CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result {
repository.sync()
return Result.success()
}
}📝 Tóm tắt
| Feature | Description |
|---|---|
| OneTimeWorkRequest | Chạy một lần |
| PeriodicWorkRequest | Chạy định kỳ (min 15 phút) |
| Constraints | Điều kiện để chạy |
| Chaining | Nối nhiều work |
| Result.retry() | Thử lại nếu fail |
| setExpedited() | Ưu tiên cao |
Last updated on