Android WorkManager 2026: Hintergrundaufgaben, Constraints und Interview-Fragen
Umfassende Anleitung zu Android WorkManager 2.11/2.12 mit Kotlin-Codebeispielen, Constraints-Konfiguration, Verkettung von Arbeitsaufträgen und häufigen Fragen aus technischen Vorstellungsgesprächen.

Android WorkManager übernimmt aufschiebbare, garantierte Hintergrundarbeiten, die App-Neustarts und Geräteneustarts überdauern. Als Teil von Android Jetpack veröffentlicht, bietet WorkManager 2.11.2 (stabil, März 2026) eine einheitliche API für API 23+, die je nach API-Level des Geräts automatisch die beste zugrunde liegende Implementierung wählt (JobScheduler, AlarmManager oder Firebase JobDispatcher).
WorkManager eignet sich für Aufgaben, die garantierte Ausführung benötigen: Hochladen von Logs, Synchronisieren von Daten, Verarbeiten von Bildern. Für sofortige Arbeiten, die den Prozess-Tod nicht überleben, sollten stattdessen Kotlin Coroutines verwendet werden.
WorkManager 2.11 in einem Kotlin-Projekt einrichten
Bevor ein Worker geschrieben wird, muss die Abhängigkeit deklariert werden. WorkManager 2.11+ erfordert minSdk 23 und compileSdk 33.
dependencies {
val workVersion = "2.11.2"
implementation("androidx.work:work-runtime-ktx:$workVersion")
// Optional: for testing
androidTestImplementation("androidx.work:work-testing:$workVersion")
}Das work-runtime-ktx-Artefakt enthält Kotlin-Erweiterungen und Coroutine-Unterstützung durch CoroutineWorker. Für die grundlegende Nutzung ist keine zusätzliche Konfiguration erforderlich: WorkManager initialisiert sich selbst über einen ContentProvider.
Einen einfachen Worker mit doWork() erstellen
Eine Worker-Klasse überschreibt doWork() und gibt ein Result zurück. Das System führt diese Methode auf einem Hintergrund-Thread aus.
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()
}
}
}Drei mögliche Ergebnisse existieren: Result.success() markiert die Fertigstellung, Result.failure() stoppt Wiederholungsversuche, und Result.retry() plant gemäß der Backoff-Richtlinie neu.
CoroutineWorker für Suspend-Funktionen verwenden
Wenn die Arbeit Suspend-Funktionen umfasst (Retrofit-Aufrufe, Room-Abfragen), eliminiert CoroutineWorker verschachtelte Callbacks.
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() läuft auf Dispatchers.Default. Um Dispatcher zu wechseln, wird withContext() innerhalb der Funktion verwendet.
Constraints für bedingte Ausführung definieren
Constraints verhindern, dass Arbeit ausgeführt wird, bis Bedingungen erfüllt sind. Dies spart Batterie und vermeidet fehlgeschlagene Versuche.
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
)
}Verfügbare Constraints umfassen Netzwerktyp (CONNECTED, UNMETERED, METERED, NOT_ROAMING), Batteriestand, Ladezustand, Speicherplatz und Gerät-Idle-Zustand. WorkManager 2.10+ akzeptiert auch einen rohen NetworkRequest für feinkörnige Netzwerkkontrolle.
Eine häufige Interview-Frage betrifft die Unterscheidung zwischen KEEP und REPLACE in enqueueUniqueWork. KEEP ignoriert neue Anfragen, wenn Arbeit mit demselben Namen existiert, REPLACE bricht bestehende Arbeit ab und startet neu. KEEP eignet sich für Uploads, bei denen Duplikate Bandbreite verschwenden, REPLACE für Synchronisierungen, bei denen nur die neuesten Daten relevant sind.
Arbeitsaufträge mit then() und combine() verketten
Komplexe Workflows erfordern sequentielle und parallele Ausführung. WorkManager verkettet Arbeitsaufträge mit beginWith() und 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()
}Ausgaben von parallelen Workern werden standardmäßig in einem ArrayCreatingInputMerger zusammengeführt. Der nächste Worker erhält alle Key-Value-Paare, wobei Arrays für doppelte Schlüssel erstellt werden.
Bereit für deine Android-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
Periodische Arbeit mit PeriodicWorkRequest planen
PeriodicWorkRequest führt wiederholt mit einem Mindestintervall von 15 Minuten aus (Android erzwingt diese Grenze).
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
)
}Das Flex-Fenster ermöglicht es WorkManager, Arbeit mit anderen Jobs zu bündeln, was die Batterieeffizienz verbessert. Ein 6-Stunden-Intervall mit 30-Minuten-Flex bedeutet, dass die Ausführung irgendwann zwischen 5:30 und 6:00 nach dem vorherigen Lauf erfolgt.
Arbeitsstatus mit LiveData und Flow beobachten
WorkManager stellt den Arbeitsstatus über WorkInfo bereit. Die Beobachtung erfolgt mit LiveData oder 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 -> {}
}
}Arbeitszustände folgen einem Lebenszyklus: ENQUEUED, RUNNING, SUCCEEDED/FAILED/CANCELLED, BLOCKED (wartet auf Abhängigkeiten).
WorkManager 2.12 Work Metrics API
WorkManager 2.12.0-rc01 (August 2026) führt WorkMetricsInfo zur Verfolgung der Ausführungshistorie ein.
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 } }
}
}Metriken umfassen Ausführungsdauer, Wiederholungszähler, Stoppgründe und Zeitstempel. Die API ermöglicht das Debuggen von intermittierenden Fehlern in der Produktion ohne benutzerdefinierte Logging-Infrastruktur.
WorkMetricsInfo-Daten werden standardmäßig nach 7 Tagen gelöscht. Die Aufbewahrung kann mit Configuration.Builder().setWorkMetricsRetentionDuration() konfiguriert werden.
Worker mit WorkManagerTestInitHelper testen
Unit-Tests für Worker erfordern das work-testing-Artefakt. TestListenableWorkerBuilder erstellt Worker ohne sie einzureihen.
@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())
}
}Für Integrationstests, die Constraints und Verkettung verifizieren, wird TestDriver verwendet, um die Erfüllung von Constraints und den Zeitablauf zu simulieren.
Häufige Interview-Fragen zu WorkManager
Technische Vorstellungsgespräche testen häufig das Verständnis der Garantien und Trade-offs von WorkManager. Diese Fragen erscheinen in Android Mid-Level- und Senior-Interviews.
F: Was passiert mit einem WorkRequest, wenn die App beendet wird?
WorkManager speichert Arbeitsaufträge in einer Room-Datenbank persistent. Wenn die App neu startet oder das System die Erfüllung von Constraints signalisiert, wird ausstehende Arbeit fortgesetzt. Diese Garantie unterscheidet WorkManager von CoroutineScope-Arbeit, die mit dem Prozess stirbt.
F: Wie unterscheidet sich WorkManager von AlarmManager?
AlarmManager plant zeitgenaue Alarme und führt Code zu bestimmten Uhrzeiten aus. WorkManager plant aufschiebbare Arbeit, die ausgeführt wird, wenn Constraints erfüllt sind, ohne Garantie für exaktes Timing. AlarmManager wird für benutzerorientierte Alarme verwendet, WorkManager für Hintergrunddatenverarbeitung.
F: Kann WorkManager Arbeit sofort ausführen?
Ja, mit setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST). Beschleunigte Arbeitsaufträge nutzen den Vordergrund-Service-Slot auf API 31+ oder JobScheduler.setImportantWhileForeground() auf älteren APIs. Quoten-Limits gelten, daher definiert die OutOfQuotaPolicy das Fallback-Verhalten.
F: Wie würden Sie alle ausstehenden Uploads abbrechen?
WorkManager.getInstance(context).cancelAllWorkByTag("upload")Tags ermöglichen Massenoperationen. Alternativ bricht cancelUniqueWork("name") eine bestimmte Kette ab.
Systeminduzierte Stopps mit Backoff-Richtlinien behandeln
Wenn das System einen Worker stoppt (Batterieoptimierung, Speicherdruck), bestimmt die Backoff-Richtlinie das Timing für Wiederholungsversuche.
val request = OneTimeWorkRequestBuilder<UploadWorker>()
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
Duration.ofSeconds(30) // Initial delay, minimum 10 seconds
)
.setBackoffOnSystemInterruption(true) // New in 2.11
.build()Exponentieller Backoff verdoppelt die Verzögerung bei jedem Wiederholungsversuch: 30s, 60s, 120s, bis zu einem Maximum von 5 Stunden. Linearer Backoff addiert die Anfangsverzögerung jedes Mal: 30s, 60s, 90s. Das neue setBackoffOnSystemInterruption()-Flag in WorkManager 2.11 wendet Backoff auch an, wenn das System (nicht der Worker) den Stopp verursacht hat.
Fang an zu üben!
Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.
Wichtige Erkenntnisse für den produktiven WorkManager-Einsatz
CoroutineWorkergegenüberWorkerbevorzugen, wenn Suspend-Funktionen aufgerufen werden. Es vermeidet das Blockieren von Threads und integriert sich mit bestehendem Coroutine-Code.enqueueUniqueWork()verwenden, um doppelte Arbeit zu verhindern.KEEPfür idempotente Operationen wählen,REPLACEwenn nur die neueste Anfrage relevant ist.- Aussagekräftige Tags auf jeder Anfrage setzen. Tags ermöglichen Beobachtung, Abbruch und Debugging über Arbeitsketten hinweg.
- Worker isoliert mit
TestListenableWorkerBuildertesten, dann Ketten mitTestDriver-Constraint-Simulation testen. - Die
WorkMetricsInfo-API von WorkManager 2.12 bietet Ausführungshistorie. Sie wird verwendet, um Wiederholungsmuster und Stoppgründe in der Produktion zu debuggen. - Constraints sparen Batterie. Ein Worker, der wegen fehlendem Netzwerk fehlschlägt, verschwendet CPU-Zyklen und verbraucht Strom bei Wiederholungsversuchen.
- Das 15-Minuten-Minimum für periodische Arbeit wird von Android erzwungen, nicht von WorkManager. Periodische Aufgaben sollten so konzipiert werden, dass sie dieses Intervall tolerieren.
Findest du den Bug in Android?
Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Geschrieben von
Anthony Fillion-MailletGründer von SharpSkill
Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.
Aktualisiert am 22. August 2026
Tags
Teilen
Verwandte Artikel

Jetpack Navigation Compose 2026: Typsichere Navigation und Interviewfragen
Ein umfassender Leitfaden zur typsicheren Navigation mit Jetpack Compose. Mit praktischen Codebeispielen, Best Practices und häufigen Interviewfragen für Android-Entwickler.

Kotlin Flow vs StateFlow vs SharedFlow: Android-Interviewfragen 2026
Die Fragen zu Kotlin Flow vs StateFlow vs SharedFlow, die Android-Interviewer 2026 stellen – mit klaren Antworten, Vergleichstabelle und produktionsreifem Code.

Android 16 im Jahr 2026: Neue APIs, Desktop-Modus und Interviewfragen
Android 16 bringt tiefgreifende Änderungen für Entwickler: Edge-to-Edge-Pflicht, Desktop-Modus, ProgressStyle-Benachrichtigungen und predictive Back-Navigation. Ein technischer Deep-Dive mit Codebeispielen und Interviewfragen.