Skip to Content

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 CaseSolution
Sync data định kỳWorkManager
Upload logsWorkManager
Download filesWorkManager
Immediate user taskCoroutines
Foreground taskForeground 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

FeatureDescription
OneTimeWorkRequestChạy một lần
PeriodicWorkRequestChạy định kỳ (min 15 phút)
ConstraintsĐiều kiện để chạy
ChainingNối nhiều work
Result.retry()Thử lại nếu fail
setExpedited()Ưu tiên cao
Last updated on