WorkManager – Arbeit, die auch läuft, wenn die App zu ist
Daten hochladen, nachts synchronisieren, eine Erinnerung schicken: Sobald etwas laufen soll, während die App geschlossen ist, ist WorkManager das richtige Werkzeug – und nicht ein Thread, den das System sowieso abräumt.
Eine Frage, die in fast jedem App-Projekt kommt: „Kann die App im Hintergrund automatisch synchronisieren?" Die Antwort ist ja — aber nicht mit einer Coroutine, die man beim Schließen der App einfach weiterlaufen lässt. Android beendet den Prozess, sobald es Speicher braucht, und dann ist die Arbeit weg.
WorkManager ist der offizielle Weg für Arbeit, die *garantiert* irgendwann erledigt wird, auch über einen Neustart des Geräts hinweg.
Ein Worker
class SyncWorker(
ctx: Context,
params: WorkerParameters
) : CoroutineWorker(ctx, params) {
override suspend fun doWork(): Result {
return try {
val anzahl = repository.syncPending() // ganz normale suspend-Funktion
Result.success(workDataOf("anzahl" to anzahl))
} catch (e: IOException) {
Result.retry() // Netz weg → später nochmal, mit Backoff
} catch (e: Exception) {
Result.failure() // wird nicht besser → aufgeben
}
}
}Der Unterschied zwischen retry() und failure() ist der eigentliche Kern. retry() heißt „das war Pech" — WorkManager versucht es später erneut und wartet dabei jedes Mal länger. failure() heißt „das ist kaputt" — nicht nochmal probieren. Wer beides verwechselt, bekommt entweder Endlosschleifen oder verlorene Daten.
Einmalig anstoßen – mit Bedingungen
val bedingungen = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
val anfrage = OneTimeWorkRequestBuilder<SyncWorker>()
.setConstraints(bedingungen)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
.build()
WorkManager.getInstance(context).enqueue(anfrage)Die Constraints sind der angenehmste Teil: Du beschreibst, *wann* die Arbeit sinnvoll ist, und musst dich nicht selbst darum kümmern. Kein Netz? WorkManager wartet. Akku fast leer? Wartet auch.
Regelmäßig wiederholen
val taeglich = PeriodicWorkRequestBuilder<SyncWorker>(6, TimeUnit.HOURS)
.setConstraints(bedingungen)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"sync",
ExistingPeriodicWorkPolicy.KEEP, // schon geplant? dann nicht doppelt anlegen
taeglich
)Zwei Fallen hier:
- Das Minimum sind 15 Minuten. Kürzere Intervalle akzeptiert das System nicht, und das ist gut so.
- Die Zeit ist nie exakt. Android bündelt solche Aufträge, um den Akku zu schonen. „Alle 6 Stunden" heißt „ungefähr alle 6 Stunden". Wenn etwas auf die Minute genau passieren muss, ist WorkManager das falsche Werkzeug — dann brauchst du einen
AlarmManagermit exaktem Alarm und einen guten Grund dafür.
Den Fortschritt in der UI anzeigen
val status by WorkManager.getInstance(context)
.getWorkInfosForUniqueWorkLiveData("sync")
.observeAsState()
when (status?.firstOrNull()?.state) {
WorkInfo.State.RUNNING -> CircularProgressIndicator()
WorkInfo.State.SUCCEEDED -> Text("Alles synchronisiert ✓")
else -> {}
}Meine Faustregel
Sobald die Frage lautet „läuft das auch, wenn die App zu ist?", ist die Antwort WorkManager. Alles, was nur läuft, solange der Nutzer hinschaut, gehört dagegen in eine Coroutine im ViewModel — dort ist WorkManager unnötiger Ballast.
Du überlegst, ob dein App-Projekt so eine Synchronisation braucht? Melde dich, ich sage dir ehrlich, ob sich der Aufwand lohnt.
Quellen
Du brauchst mehr als ein Snippet?
Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.
Projekt anfragen →Verwandte Snippets
callbackFlow – Listener-APIs in einen Flow verwandeln
Android steckt voller Callback-APIs mit `register`/`unregister`. Mit `callbackFlow` machst du daraus einen ganz normalen Flow – inklusive automatischem Abmelden, wenn niemand mehr zuhört.
Fehler in Flows abfangen – catch, retry und ein sinnvoller Fallback
Ein try/catch um einen Flow herum fängt nichts. Fehler laufen im Flow selbst mit und werden dort behandelt – mit catch für die Ausgabe und retry für den zweiten Versuch bei Netzproblemen.
rememberUpdatedState in Compose – wenn dein Callback im Effekt einfriert
Ein LaunchedEffect mit key1 = Unit startet genau einmal – und hält damit für immer die Lambda-Referenz vom ersten Durchlauf fest. Klickt der Nutzer später etwas anderes an, passiert das Falsche. rememberUpdatedState löst genau das.