MWCodebymw.de ↗
Kotlin

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 AlarmManager mit 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

#WorkManager#Android#Hintergrund#Kotlin#Synchronisation

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 →