Android WorkManager у 2026: Фонові Завдання, Обмеження та Питання на Співбесіду
Повний посібник з WorkManager 2.11 на Kotlin - від базового налаштування через обмеження та ланцюги завдань до тестування та питань на технічних співбесідах.

Android WorkManager обробляє відкладені, гарантовані фонові завдання, які зберігаються після перезапуску додатка та перезавантаження пристрою. Випущений як частина Android Jetpack, WorkManager 2.11.2 (стабільна версія, березень 2026) надає уніфікований API, що працює на API 23+, автоматично обираючи найкращу реалізацію (JobScheduler, AlarmManager або Firebase JobDispatcher) залежно від рівня API пристрою.
WorkManager слід використовувати для завдань, що вимагають гарантованого виконання: надсилання логів, синхронізація даних, обробка зображень. Для негайної роботи, яка не повинна переживати смерть процесу, краще підходять Kotlin Coroutines.
Налаштування WorkManager 2.11 у Kotlin Проекті
Перед написанням будь-якого worker потрібно оголосити залежність. WorkManager 2.11+ вимагає minSdk 23 та compileSdk 33.
dependencies {
val workVersion = "2.11.2"
implementation("androidx.work:work-runtime-ktx:$workVersion")
// Optional: for testing
androidTestImplementation("androidx.work:work-testing:$workVersion")
}Артефакт work-runtime-ktx містить розширення Kotlin та підтримку Coroutines через CoroutineWorker. Додаткова конфігурація для базового використання не потрібна: WorkManager ініціалізується автоматично через ContentProvider.
Створення Простого Worker з doWork()
Клас Worker перевизначає метод doWork() і повертає Result. Система виконує цей метод у фоновому потоці.
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()
}
}
}Існує три можливі результати: Result.success() позначає завершення, Result.failure() зупиняє повторні спроби, а Result.retry() планує повторне виконання відповідно до політики backoff.
Використання CoroutineWorker для Suspend Функцій
Коли робота включає suspend функції (виклики Retrofit, запити Room), CoroutineWorker усуває вкладення callback.
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() виконується на Dispatchers.Default. Для зміни dispatcher використовується withContext() всередині функції.
Визначення Обмежень для Умовного Виконання
Обмеження запобігають виконанню роботи до виконання умов. Це економить батарею та уникає невдалих спроб.
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
)
}Доступні обмеження включають тип мережі (CONNECTED, UNMETERED, METERED, NOT_ROAMING), рівень батареї, стан зарядки, доступний простір на диску та стан простою пристрою. WorkManager 2.10+ також приймає сирий NetworkRequest для точного контролю мережі.
Поширене питання на співбесіді стосується різниці між KEEP та REPLACE у enqueueUniqueWork. KEEP ігнорує нові запити, якщо робота з такою назвою вже існує, REPLACE скасовує існуючу роботу і починає заново. KEEP використовується для завантажень, де дублікати марнують пропускну здатність, REPLACE для синхронізації, де важливі лише останні дані.
Ланцюгування Запитів Роботи з then() та combine()
Складні робочі процеси вимагають послідовного та паралельного виконання. WorkManager ланцюгує запити роботи за допомогою beginWith() та 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()
}Вивід з паралельних worker об'єднується за замовчуванням через ArrayCreatingInputMerger. Наступний worker отримує всі пари ключ-значення, при цьому для дублікатів ключів створюються масиви.
Готовий до співбесід з Android?
Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.
Планування Періодичної Роботи з PeriodicWorkRequest
PeriodicWorkRequest виконується повторно з мінімальним інтервалом 15 хвилин (Android примусово застосовує цей ліміт).
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
)
}Вікно гнучкості дозволяє WorkManager групувати роботу з іншими завданнями, покращуючи ефективність батареї. 6-годинний інтервал з 30-хвилинним вікном гнучкості означає, що виконання відбудеться між 5:30 та 6:00 після попереднього запуску.
Спостереження за Станом Роботи з LiveData та Flow
WorkManager надає стан роботи через WorkInfo. Його можна спостерігати за допомогою LiveData або 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 -> {}
}
}Стани роботи слідують життєвому циклу: ENQUEUED, RUNNING, SUCCEEDED/FAILED/CANCELLED, BLOCKED (очікування залежностей).
WorkManager 2.12 Work Metrics API
WorkManager 2.12.0-rc01 (серпень 2026) вводить WorkMetricsInfo для відстеження історії виконання.
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 } }
}
}Метрики включають тривалість виконання, лічильники повторних спроб, причини зупинки та мітки часу. API дозволяє відлагоджувати періодичні збої у продакшені без власної інфраструктури логування.
Дані WorkMetricsInfo за замовчуванням видаляються через 7 днів. Налаштуйте зберігання через Configuration.Builder().setWorkMetricsRetentionDuration().
Тестування Worker з WorkManagerTestInitHelper
Юніт-тестування worker вимагає артефакту work-testing. TestListenableWorkerBuilder створює worker без їх додавання в чергу.
@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())
}
}Для інтеграційних тестів, що перевіряють обмеження та ланцюгування, TestDriver використовується для симуляції виконання обмежень та плину часу.
Поширені Питання про WorkManager на Співбесіді
Технічні співбесіди часто тестують розуміння гарантій та компромісів WorkManager. Ці питання з'являються на співбесідах для Android розробників рівня middle та senior.
П: Що відбувається з WorkRequest, коли додаток закривається?
WorkManager зберігає запити роботи в базі даних Room. Коли додаток перезапускається або система сигналізує про виконання обмежень, очікувана робота відновлюється. Ця гарантія відрізняє WorkManager від роботи в CoroutineScope, яка помирає разом з процесом.
П: Чим WorkManager відрізняється від AlarmManager?
AlarmManager планує точні за часом будильники та виконує код у визначені моменти часу. WorkManager планує відкладену роботу, яка виконується при виконанні обмежень, без гарантії точного часу. AlarmManager використовується для будильників, видимих користувачеві, WorkManager для фонової обробки даних.
П: Чи може WorkManager виконати роботу негайно?
Так, з setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST). Прискорені запити роботи використовують слот foreground service на API 31+ або JobScheduler.setImportantWhileForeground() на старіших API. Застосовуються обмеження квоти, тому OutOfQuotaPolicy визначає резервну поведінку.
П: Як скасувати всі очікувані завантаження?
WorkManager.getInstance(context).cancelAllWorkByTag("upload")Теги дозволяють масові операції. Альтернативно, cancelUniqueWork("name") скасовує конкретний ланцюг.
Обробка Системних Зупинок з Політиками Backoff
Коли система зупиняє worker (оптимізація батареї, навантаження пам'яті), політика backoff визначає час повторної спроби.
val request = OneTimeWorkRequestBuilder<UploadWorker>()
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
Duration.ofSeconds(30) // Initial delay, minimum 10 seconds
)
.setBackoffOnSystemInterruption(true) // New in 2.11
.build()Експоненційний backoff подвоює затримку при кожній повторній спробі: 30с, 60с, 120с, до максимуму 5 годин. Лінійний backoff додає початкову затримку кожного разу: 30с, 60с, 90с. Новий прапорець setBackoffOnSystemInterruption() у WorkManager 2.11 застосовує backoff навіть коли зупинку спричинила система (не worker).
Починай практикувати!
Перевір свої знання з нашими симуляторами співбесід та технічними тестами.
Ключові Висновки для Використання WorkManager у Продакшені
- Віддавайте перевагу
CoroutineWorkerнадWorkerпри виклику suspend функцій. Це уникає блокування потоків та інтегрується з існуючим кодом coroutine. - Використовуйте
enqueueUniqueWork()для запобігання дублювання роботи. ОбирайтеKEEPдля ідемпотентних операцій,REPLACEколи важливий лише останній запит. - Встановлюйте значущі теги на кожному запиті. Теги дозволяють спостереження, скасування та відлагодження в ланцюгах роботи.
- Тестуйте worker ізольовано з
TestListenableWorkerBuilder, потім тестуйте ланцюги з симуляцією обмеженьTestDriver. - API
WorkMetricsInfoу WorkManager 2.12 надає історію виконання. Використовуйте його для відлагодження патернів повторних спроб та причин зупинки у продакшені. - Обмеження економлять батарею. Worker, який зазнає невдачі через відсутність мережі, витрачає цикли CPU та споживає енергію при повторних спробах.
- 15-хвилинний мінімум для періодичної роботи встановлюється Android, а не WorkManager. Періодичні завдання слід проектувати з урахуванням цього інтервалу.
Чи знайдеш ти помилку в Android?
Справжній фрагмент коду, прихована помилка, одна спроба на день. Щоб спробувати, акаунт не потрібен.

Автор:
Anthony Fillion-MailletЗасновник SharpSkill
Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.
Оновлено 22 серпня 2026 р.
Теги
Поділитися
Пов'язані статті

Jetpack Navigation Compose у 2026: Type-Safe Навігація та Питання на Співбесіді
Комплексний посібник з Jetpack Navigation Compose з type-safe навігацією, розширеними патернами та питаннями для співбесід Android-розробників.

Room Database в Android 2026: Міграції, Зв'язки та Coroutines
Повний посібник з Room Database в Android 2026 - від міграцій схеми, через зв'язки між сутностями, до інтеграції з Kotlin Coroutines та найкращих практик.

Kotlin Flow проти StateFlow проти SharedFlow: питання на Android-співбесіді у 2026
Питання про Kotlin Flow проти StateFlow проти SharedFlow, які Android-інтерв'юери ставлять у 2026 році, з чіткими відповідями, таблицею порівняння та готовим до продакшену кодом.