Android WorkManager 2026: Hướng Dẫn Toàn Diện Background Tasks, Constraints và Câu Hỏi Phỏng Vấn
Tìm hiểu Android WorkManager để chạy background tasks với constraints, chaining và CoroutineWorker. Bao gồm các câu hỏi phỏng vấn Android developer thường gặp.

Android WorkManager xử lý các công việc background có thể trì hoãn nhưng được đảm bảo thực thi, ngay cả sau khi ứng dụng khởi động lại hoặc thiết bị reboot. Là một phần của Android Jetpack, WorkManager 2.11.2 (phiên bản ổn định, tháng 3 năm 2026) cung cấp API thống nhất hoạt động trên API 23+ bằng cách chọn implementation tốt nhất (JobScheduler, AlarmManager, hoặc Firebase JobDispatcher) dựa trên API level của thiết bị.
Sử dụng WorkManager cho các tác vụ cần đảm bảo thực thi: tải log lên server, đồng bộ dữ liệu, xử lý hình ảnh. Đối với công việc cần thực hiện ngay và không cần tồn tại khi process chết, hãy sử dụng Kotlin Coroutines.
Thiết Lập WorkManager 2.11 Trong Dự Án Kotlin
Trước khi viết bất kỳ worker nào, dependency cần được khai báo. WorkManager 2.11+ yêu cầu minSdk 23 và compileSdk 33.
dependencies {
val workVersion = "2.11.2"
implementation("androidx.work:work-runtime-ktx:$workVersion")
// Optional: for testing
androidTestImplementation("androidx.work:work-testing:$workVersion")
}Artifact work-runtime-ktx bao gồm Kotlin extensions và hỗ trợ Coroutine thông qua CoroutineWorker. Không cần cấu hình bổ sung cho việc sử dụng cơ bản: WorkManager tự khởi tạo thông qua ContentProvider.
Tạo Worker Đơn Giản Với doWork()
Lớp Worker override doWork() và trả về Result. Hệ thống chạy method này trên background thread.
class SyncWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
override fun doWork(): Result {
// Read input data
val userId = inputData.getString("user_id") ?: return Result.failure()
return try {
// Perform sync operation
val syncService = SyncService.getInstance(applicationContext)
syncService.syncUserData(userId)
Result.success()
} catch (e: IOException) {
// Retry on network errors
Result.retry()
} catch (e: Exception) {
// Permanent failure
Result.failure()
}
}
}Có ba kết quả có thể xảy ra: Result.success() đánh dấu hoàn thành, Result.failure() dừng việc retry, và Result.retry() lên lịch lại theo chính sách backoff.
Sử Dụng CoroutineWorker Cho Suspend Functions
Khi công việc liên quan đến suspend functions (gọi Retrofit, truy vấn Room), CoroutineWorker loại bỏ callback lồng nhau.
class UploadWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val imageUri = inputData.getString("image_uri")
?: return Result.failure()
// Progress reporting (visible in WorkInfo observers)
setProgress(workDataOf("status" to "compressing"))
val compressed = ImageCompressor.compress(imageUri)
setProgress(workDataOf("status" to "uploading"))
return try {
val response = ApiClient.imageService.upload(compressed)
val outputData = workDataOf("url" to response.imageUrl)
Result.success(outputData)
} catch (e: HttpException) {
if (e.code() in 500..599) Result.retry()
else Result.failure()
}
}
}CoroutineWorker.doWork() chạy trên Dispatchers.Default. Để chuyển dispatcher, sử dụng withContext() bên trong hàm.
Định Nghĩa Constraints Cho Thực Thi Có Điều Kiện
Constraints ngăn công việc chạy cho đến khi các điều kiện được đáp ứng. Điều này tiết kiệm pin và tránh các lần thử thất bại.
fun scheduleUpload(context: Context, imageUri: String) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED) // WiFi only
.setRequiresBatteryNotLow(true)
.setRequiresStorageNotLow(true)
.build()
val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setConstraints(constraints)
.setInputData(workDataOf("image_uri" to imageUri))
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
Duration.ofMinutes(1)
)
.addTag("upload")
.build()
WorkManager.getInstance(context)
.enqueueUniqueWork(
"upload_$imageUri",
ExistingWorkPolicy.KEEP,
uploadRequest
)
}Các constraints có sẵn bao gồm loại mạng (CONNECTED, UNMETERED, METERED, NOT_ROAMING), mức pin, trạng thái sạc, dung lượng lưu trữ, và trạng thái idle của thiết bị. WorkManager 2.10+ cũng chấp nhận NetworkRequest thô để kiểm soát mạng chi tiết hơn.
Một câu hỏi phỏng vấn phổ biến là khi nào sử dụng KEEP so với REPLACE trong enqueueUniqueWork. KEEP bỏ qua yêu cầu mới nếu công việc cùng tên đã tồn tại, REPLACE hủy công việc hiện có và bắt đầu lại. Sử dụng KEEP cho upload khi bản sao lãng phí băng thông, REPLACE cho đồng bộ khi chỉ dữ liệu mới nhất quan trọng.
Chuỗi Work Requests Với then() và combine()
Các workflow phức tạp yêu cầu thực thi tuần tự và song song. WorkManager chuỗi work requests sử dụng beginWith() và then().
fun processAndUploadImages(context: Context, imageUris: List<String>) {
val workManager = WorkManager.getInstance(context)
// Parallel compression workers
val compressRequests = imageUris.map { uri ->
OneTimeWorkRequestBuilder<CompressWorker>()
.setInputData(workDataOf("uri" to uri))
.build()
}
// Single upload worker runs after all compressions complete
val uploadRequest = OneTimeWorkRequestBuilder<BatchUploadWorker>()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.build()
// Cleanup runs after upload, regardless of success
val cleanupRequest = OneTimeWorkRequestBuilder<CleanupWorker>()
.build()
workManager
.beginWith(compressRequests) // Parallel
.then(uploadRequest) // Sequential
.then(cleanupRequest) // Sequential
.enqueue()
}Output từ parallel workers được hợp nhất vào ArrayCreatingInputMerger theo mặc định. Worker tiếp theo nhận tất cả các cặp key-value, với array được tạo cho các key trùng lặp.
Sẵn sàng chinh phục phỏng vấn Android?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
Lên Lịch Periodic Work Với PeriodicWorkRequest
PeriodicWorkRequest thực thi lặp lại với khoảng cách tối thiểu 15 phút (Android bắt buộc giới hạn này).
fun scheduleDailySync(context: Context) {
val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(
repeatInterval = 6,
repeatIntervalTimeUnit = TimeUnit.HOURS,
flexTimeWindow = 30,
flexTimeUnit = TimeUnit.MINUTES
)
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.addTag("periodic_sync")
.build()
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork(
"daily_sync",
ExistingPeriodicWorkPolicy.UPDATE,
syncRequest
)
}Flex window cho phép WorkManager gộp công việc với các job khác, cải thiện hiệu quả pin. Khoảng cách 6 giờ với flex 30 phút có nghĩa là thực thi xảy ra trong khoảng 5:30 đến 6:00 sau lần chạy trước.
Theo Dõi Trạng Thái Công Việc Với LiveData và Flow
WorkManager expose trạng thái công việc thông qua WorkInfo. Theo dõi bằng LiveData hoặc Kotlin Flow.
class UploadViewModel(application: Application) : AndroidViewModel(application) {
private val workManager = WorkManager.getInstance(application)
// Flow-based observation
fun observeUpload(workId: UUID): Flow<WorkInfo?> {
return workManager.getWorkInfoByIdFlow(workId)
}
// Check if any upload is running
val activeUploads: Flow<List<WorkInfo>> =
workManager.getWorkInfosByTagFlow("upload")
.map { workInfos ->
workInfos.filter { it.state == WorkInfo.State.RUNNING }
}
}
// In Compose UI
@Composable
fun UploadProgress(workId: UUID, viewModel: UploadViewModel) {
val workInfo by viewModel.observeUpload(workId)
.collectAsState(initial = null)
when (workInfo?.state) {
WorkInfo.State.RUNNING -> {
val status = workInfo?.progress?.getString("status") ?: "working"
CircularProgressIndicator()
Text(status)
}
WorkInfo.State.SUCCEEDED -> {
val url = workInfo?.outputData?.getString("url")
Text("Uploaded: $url")
}
WorkInfo.State.FAILED -> Text("Upload failed")
else -> {}
}
}Các trạng thái công việc tuân theo lifecycle: ENQUEUED, RUNNING, SUCCEEDED/FAILED/CANCELLED, BLOCKED (đang chờ dependencies).
API Work Metrics của WorkManager 2.12
WorkManager 2.12.0-rc01 (tháng 8 năm 2026) giới thiệu WorkMetricsInfo để theo dõi lịch sử thực thi.
class MetricsRepository(private val context: Context) {
private val workManager = WorkManager.getInstance(context)
suspend fun getWorkerMetrics(workerName: String): List<WorkMetricsInfo> {
val query = WorkMetricsQuery.Builder()
.setWorkerClassName(workerName)
.setLimit(100)
.build()
return workManager.getWorkMetrics(query)
}
fun analyzeFailures(metrics: List<WorkMetricsInfo>): Map<Int, Int> {
// Count stop reasons
return metrics
.flatMap { it.stopReasonCounts.entries }
.groupBy { it.key }
.mapValues { entry -> entry.value.sumOf { it.value } }
}
}Metrics bao gồm thời gian thực thi, số lần retry, lý do dừng, và timestamps. API này cho phép debug các lỗi không liên tục trong production mà không cần hạ tầng logging tùy chỉnh.
Dữ liệu WorkMetricsInfo được xóa sau 7 ngày theo mặc định. Cấu hình thời gian lưu giữ với Configuration.Builder().setWorkMetricsRetentionDuration().
Kiểm Thử Workers Với WorkManagerTestInitHelper
Unit testing workers yêu cầu artifact work-testing. TestListenableWorkerBuilder tạo workers mà không enqueue chúng.
@RunWith(AndroidJUnit4::class)
class SyncWorkerTest {
private lateinit var context: Context
@Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
val config = Configuration.Builder()
.setMinimumLoggingLevel(Log.DEBUG)
.setExecutor(SynchronousExecutor())
.build()
WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
}
@Test
fun syncWorker_withValidUserId_succeeds() = runTest {
val inputData = workDataOf("user_id" to "123")
val worker = TestListenableWorkerBuilder<SyncWorker>(context)
.setInputData(inputData)
.build()
val result = worker.doWork()
assertThat(result).isEqualTo(ListenableWorker.Result.success())
}
@Test
fun syncWorker_withoutUserId_fails() = runTest {
val worker = TestListenableWorkerBuilder<SyncWorker>(context)
.build()
val result = worker.doWork()
assertThat(result).isEqualTo(ListenableWorker.Result.failure())
}
}Đối với integration tests xác minh constraints và chaining, sử dụng TestDriver để mô phỏng việc đáp ứng constraint và thời gian trôi qua.
Các Câu Hỏi Phỏng Vấn Phổ Biến Về WorkManager
Các cuộc phỏng vấn kỹ thuật thường kiểm tra sự hiểu biết về các đảm bảo và đánh đổi của WorkManager. Những câu hỏi này xuất hiện trong các cuộc phỏng vấn Android cấp trung và cấp cao.
H: Điều gì xảy ra với WorkRequest khi ứng dụng bị kill?
WorkManager lưu trữ work requests trong database Room. Khi ứng dụng khởi động lại hoặc hệ thống báo hiệu rằng constraint được đáp ứng, công việc đang chờ được tiếp tục. Sự đảm bảo này phân biệt WorkManager với công việc CoroutineScope chết cùng với process.
H: WorkManager khác với AlarmManager như thế nào?
AlarmManager lên lịch alarm với thời gian chính xác và chạy code vào thời điểm đồng hồ cụ thể. WorkManager lên lịch công việc có thể trì hoãn chạy khi constraints được đáp ứng, không có đảm bảo thời gian chính xác. Sử dụng AlarmManager cho alarm hướng đến người dùng, WorkManager cho xử lý dữ liệu background.
H: WorkManager có thể chạy công việc ngay lập tức không?
Có, với setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST). Expedited work requests sử dụng slot foreground service trên API 31+ hoặc JobScheduler.setImportantWhileForeground() trên các API cũ hơn. Giới hạn quota áp dụng, vì vậy OutOfQuotaPolicy định nghĩa hành vi fallback.
H: Làm thế nào để hủy tất cả pending uploads?
WorkManager.getInstance(context).cancelAllWorkByTag("upload")Tags cho phép các thao tác hàng loạt. Ngoài ra, cancelUniqueWork("name") hủy một chain cụ thể.
Xử Lý System-Induced Stops Với Backoff Policies
Khi hệ thống dừng worker (tối ưu hóa pin, áp lực bộ nhớ), chính sách backoff xác định thời gian retry.
val request = OneTimeWorkRequestBuilder<UploadWorker>()
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
Duration.ofSeconds(30) // Initial delay, minimum 10 seconds
)
.setBackoffOnSystemInterruption(true) // New in 2.11
.build()Exponential backoff nhân đôi delay sau mỗi lần retry: 30s, 60s, 120s, tối đa 5 giờ. Linear backoff thêm delay ban đầu mỗi lần: 30s, 60s, 90s. Flag setBackoffOnSystemInterruption() mới trong WorkManager 2.11 áp dụng backoff ngay cả khi hệ thống (không phải worker) gây ra việc dừng.
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Những Điểm Quan Trọng Cho Việc Sử Dụng WorkManager Trong Production
- Ưu tiên
CoroutineWorkerhơnWorkerkhi gọi suspend functions. Điều này tránh blocking threads và tích hợp với code coroutine hiện có. - Sử dụng
enqueueUniqueWork()để ngăn công việc trùng lặp. ChọnKEEPcho các thao tác idempotent,REPLACEkhi chỉ yêu cầu mới nhất quan trọng. - Đặt tags có ý nghĩa cho mỗi request. Tags cho phép quan sát, hủy bỏ, và debug trên toàn bộ work chains.
- Kiểm thử workers độc lập với
TestListenableWorkerBuilder, sau đó kiểm thử chains với mô phỏng constraint củaTestDriver. - API
WorkMetricsInfocủa WorkManager 2.12 cung cấp lịch sử thực thi. Sử dụng nó để debug các mẫu retry và lý do dừng trong production. - Constraints tiết kiệm pin. Worker thất bại do không có mạng lãng phí chu kỳ CPU và tiêu hao năng lượng trong các lần retry.
- Tối thiểu 15 phút cho periodic work được Android bắt buộc, không phải WorkManager. Thiết kế các tác vụ định kỳ để chịu được khoảng cách này.
Bạn có tìm ra lỗi trong Android không?
Một đoạn mã thật, một lỗi ẩn, mỗi ngày một lượt. Không cần tài khoản để thử.

Viết bởi
Anthony Fillion-MailletNgười sáng lập SharpSkill
Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.
Cập nhật ngày 22 tháng 8, 2026
Chia sẻ
Bài viết liên quan

Modularization Android 2026: Kiến Trúc Multi-Module và Câu Hỏi Phỏng Vấn
Làm chủ kiến trúc multi-module Android với convention plugins, Gradle version catalogs và feature modules. Bao gồm các câu hỏi phỏng vấn phổ biến về chiến lược modularization.

Android CameraX 2026: Hướng Dẫn Chụp Ảnh và Quay Video với Câu Hỏi Phỏng Vấn
Tìm hiểu CameraX trên Android 2026: chụp ảnh, quay video, tích hợp Jetpack Compose và các câu hỏi phỏng vấn kỹ thuật dành cho lập trình viên Android.

Jetpack Navigation Compose 2026: Điều Hướng Type-Safe và Câu Hỏi Phỏng Vấn
Tìm hiểu Jetpack Navigation Compose 2.10 với điều hướng type-safe sử dụng Kotlin Serialization. Hướng dẫn đầy đủ với ví dụ code và câu hỏi phỏng vấn Android.