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
Room-Migrationen – das Schema ändern, ohne Nutzerdaten zu verlieren
Eine neue Spalte in der Entity, und beim nächsten Start ist der Spielstand weg – weil fallbackToDestructiveMigration die Datenbank einfach löscht. So machst du es richtig, inklusive Test.
stateIn und shareIn – aus einem kalten Flow wird geteilter Zustand
Jeder Sammler eines kalten Flows startet die Arbeit neu – zwei Beobachter, zwei Datenbankabfragen. stateIn und shareIn machen daraus einen Strom, den sich alle teilen, samt aktuellem Wert und Abschalten bei Inaktivität.
viewModelScope – Coroutinen, die mit dem Bildschirm verschwinden
Eine Coroutine, die nach dem Schließen des Bildschirms weiterläuft, schreibt in ein ViewModel, das niemand mehr sieht – und hält im schlimmsten Fall die ganze Activity im Speicher. viewModelScope beendet sie automatisch.