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 Hintergrundaufgaben Tutorial 2026

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).

Wann WorkManager verwenden

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.

build.gradle.kts (app module)gradle
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.

SyncWorker.ktkotlin
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.

UploadWorker.ktkotlin
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.

ScheduleUpload.ktkotlin
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.

Interview-Einblick

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().

WorkChain.ktkotlin
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).

PeriodicSync.ktkotlin
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.

WorkObserver.ktkotlin
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.

MetricsExample.ktkotlin
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.

Metrik-Aufbewahrung

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.

SyncWorkerTest.ktkotlin
@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?

kotlin
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.

BackoffConfig.ktkotlin
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

  • CoroutineWorker gegenüber Worker bevorzugen, 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. KEEP für idempotente Operationen wählen, REPLACE wenn 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 TestListenableWorkerBuilder testen, dann Ketten mit TestDriver-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.
Tägliche Challenge

Findest du den Bug in Android?

Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Anthony Fillion-Maillet

Geschrieben von

Anthony Fillion-Maillet

Gründer von SharpSkill

Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.

Aktualisiert am 22. August 2026

Tags

#android
#workmanager
#kotlin
#background-tasks
#jetpack

Teilen

Verwandte Artikel