Work Requests in Android
Work Requests beschreiben geplante Hintergrundarbeit in Android. Du lernst, wann einmalige und periodische Jobs passen.
Work Requests helfen dir, Hintergrundarbeit in Android bewusst zu planen: nicht als zufälligen Thread, nicht als dauerhaft laufenden Service, sondern als beschriebene Aufgabe mit klaren Bedingungen. Du legst fest, was erledigt werden soll, ob es einmalig oder wiederkehrend ist, und unter welchen Voraussetzungen Android diese Arbeit ausführen darf.
Was ist das?
Ein Work Request ist eine konkrete Anfrage an das System, eine Einheit von Hintergrundarbeit auszuführen. In modernen Android-Apps passiert das typischerweise über WorkManager. Der Work Request selbst enthält nicht die ganze Fachlogik, sondern beschreibt den Auftrag: welcher Worker gestartet wird, welche Eingabedaten er bekommt, welche Bedingungen gelten und ob die Arbeit einmalig oder periodisch laufen soll.
Das mentale Modell ist wichtig: Du startest nicht direkt einen Hintergrundprozess, den du vollständig kontrollierst. Du übergibst Android eine geplante Aufgabe. Android berücksichtigt Akkustand, Netzwerk, App-Standby, Doze-Modus und weitere Systemregeln. Scheduling heißt deshalb nicht: „Führe exakt jetzt um 10:00 Uhr aus.“ Es heißt eher: „Führe diese Aufgabe aus, sobald die angegebenen Bedingungen erfüllt sind und das System es zulässt.“
Die zwei zentralen Varianten sind OneTimeWorkRequest und PeriodicWorkRequest. Ein OneTimeWorkRequest beschreibt Arbeit, die einmal erledigt werden soll. Beispiele sind das Hochladen eines Fotos, das Senden eines Fehlerberichts, das Synchronisieren nach einer Nutzeraktion oder das Aufräumen alter Cache-Dateien. Ein PeriodicWorkRequest beschreibt wiederkehrende Arbeit. Beispiele sind ein regelmäßiger Datenabgleich, das Aktualisieren lokaler Inhalte oder ein geplanter Wartungslauf.
Im Android-Alltag liegt Work Requests oft zwischen UI, Architektur und Betriebssystem. Eine Compose-Oberfläche löst vielleicht eine Synchronisation aus, ein ViewModel entscheidet über den Use Case, und ein Repository oder Scheduler registriert die eigentliche Arbeit. Die Ausführung selbst gehört aber nicht in die UI-Schicht. Das ist ein Qualitätsmerkmal: Hintergrundarbeit bleibt testbarer, stabiler und unabhängiger vom Lebenszyklus eines Screens.
Wie funktioniert es?
Ein Work Request verweist auf einen Worker. Der Worker enthält die Arbeit, zum Beispiel eine Netzwerkoperation, eine Datenbankbereinigung oder eine Dateioperation. Wenn du Kotlin nutzt, ist oft CoroutineWorker passend, weil du darin suspendierende APIs sauber verwenden kannst. Das fügt sich gut in moderne Android-Architektur ein: Netzwerkaufrufe, Datenbankzugriffe und Repository-Funktionen laufen ohnehin häufig mit Coroutines.
Bei einem OneTimeWorkRequest erstellst du eine einmalige Anfrage. Du kannst Constraints setzen, zum Beispiel „nur bei Netzwerkverbindung“ oder „nur wenn das Gerät nicht fast leer ist“. Du kannst auch Input-Daten übergeben, etwa eine ID oder einen Dateipfad. Wichtig ist dabei: Input-Daten sollten klein und serialisierbar sein. Große Objekte, Bitmaps oder komplexe Modelle gehören nicht direkt in den Work Request. Speichere solche Daten besser in einer Datenbank oder Datei und übergib nur einen Schlüssel.
Ein PeriodicWorkRequest arbeitet ähnlich, hat aber eine Wiederholungslogik. Du definierst ein Intervall, und WorkManager plant die Ausführung wiederkehrend. Dabei musst du akzeptieren, dass periodische Arbeit nicht sekundengenau läuft. Android bündelt Hintergrundaktivität, um Akku und Systemressourcen zu schonen. Wenn du eine exakte Uhrzeit oder einen präzisen Alarm brauchst, ist ein periodischer Work Request meist nicht das richtige Werkzeug. Für viele Sync- und Wartungsaufgaben ist diese Unschärfe aber genau richtig.
Scheduling besteht aus mehreren Bausteinen. Erstens wählst du die Art der Wiederholung: einmalig oder periodisch. Zweitens formulierst du Constraints. Drittens entscheidest du, wie mit bestehenden Aufgaben umgegangen wird. Bei eindeutiger Arbeit kannst du Namen vergeben und steuern, ob ein neuer Request den alten ersetzt, ignoriert wird oder an eine Kette angehängt wird. Das verhindert, dass deine App denselben Sync zehnmal parallel startet, nur weil der Nutzer einen Button mehrfach gedrückt hat.
Du solltest Work Requests als robuste, wiederholbare Aufträge schreiben. Android kann Arbeit später ausführen, erneut starten oder nach einem Prozessende fortsetzen. Deshalb muss dein Worker damit umgehen können, dass der Zustand der App anders ist als beim Einplanen. Prüfe im Worker aktuelle Daten erneut, verlasse dich nicht blind auf UI-Zustand, und mache Operationen möglichst idempotent. Idempotent bedeutet hier: Wenn dieselbe Aufgabe erneut läuft, entsteht kein falscher Doppelzustand.
Coroutines und Flow spielen in diesem Umfeld eine unterstützende Rolle. Coroutines helfen dir, die eigentliche Arbeit lesbar und nicht blockierend zu schreiben. Flow kann Statusänderungen liefern, etwa wenn du den Fortschritt geplanter Arbeit in der UI anzeigen möchtest. Trotzdem bleibt der Work Request selbst das Scheduling-Werkzeug. Er ersetzt nicht deine Architektur, sondern verbindet sie mit den Hintergrundregeln des Systems.
In der Praxis
Stell dir vor, deine App speichert Notizen lokal und soll sie bei verfügbarer Internetverbindung mit einem Server synchronisieren. Wenn der Nutzer eine Notiz ändert, muss der Sync nicht sofort im UI-Thread laufen. Stattdessen planst du einen OneTimeWorkRequest, der nur bei Netzwerkverbindung ausgeführt wird. Der Worker liest die offenen Änderungen aus der lokalen Datenbank, sendet sie an den Server und markiert sie danach als synchronisiert.
Ein einfacher Aufbau kann so aussehen:
class SyncNotesWorker(
appContext: Context,
params: WorkerParameters,
private val repository: NotesRepository
) : CoroutineWorker(appContext, params) {
override suspend fun doWork(): Result {
return try {
repository.syncPendingNotes()
Result.success()
} catch (exception: IOException) {
Result.retry()
} catch (exception: Exception) {
Result.failure()
}
}
}
fun scheduleNoteSync(context: Context) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val request = OneTimeWorkRequestBuilder<SyncNotesWorker>()
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueueUniqueWork(
"note-sync",
ExistingWorkPolicy.KEEP,
request
)
}
An diesem Beispiel siehst du mehrere Regeln. Der Worker ist klein und ruft eine Repository-Funktion auf. Die eigentliche Geschäftslogik liegt nicht im WorkManager-Code. Netzwerkfehler führen zu Result.retry(), weil ein späterer Versuch sinnvoll sein kann. Andere Fehler können fehlschlagen, wenn sie nicht durch Warten lösbar sind. Mit enqueueUniqueWork und dem Namen "note-sync" vermeidest du parallele Sync-Läufe für dieselbe Aufgabe.
Für wiederkehrenden Sync würdest du eher einen PeriodicWorkRequest nutzen:
fun schedulePeriodicSync(context: Context) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val request = PeriodicWorkRequestBuilder<SyncNotesWorker>(
6,
TimeUnit.HOURS
)
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"periodic-note-sync",
ExistingPeriodicWorkPolicy.UPDATE,
request
)
}
Diese Variante sagt: Die App möchte etwa alle sechs Stunden synchronisieren, wenn Netzwerk verfügbar ist. Sie sagt nicht: exakt alle sechs Stunden auf die Minute. Genau hier liegt eine typische Stolperfalle. Viele Lernende behandeln PeriodicWorkRequest wie einen Timer. Das führt zu falschen Erwartungen, besonders beim Testen auf echten Geräten. Wenn deine App präzise Erinnerungen, Wecker oder Termine braucht, musst du die passende Android-API für exakte zeitliche Anforderungen prüfen. Work Requests sind für zuverlässige, verschiebbare Hintergrundarbeit gedacht.
Eine zweite Stolperfalle ist zu viel Logik im Worker. Ein Worker sollte keine UI kennen, keine Compose-State-Objekte halten und keine Activity referenzieren. Er läuft unabhängig von Screens. Wenn du Fortschritt anzeigen möchtest, beobachtest du den Status über WorkManager und leitest ihn in dein ViewModel weiter. Die Compose-UI rendert dann nur den Zustand. So bleibt die Richtung sauber: UI löst Arbeit aus oder beobachtet Arbeit, aber sie führt die Hintergrundarbeit nicht selbst aus.
Eine dritte Stolperfalle betrifft Eingabedaten. Übergib keine kompletten Domain-Objekte in den Request. Wenn eine Notiz synchronisiert werden soll, übergib höchstens eine ID. Der Worker lädt die aktuellen Daten aus der Datenbank. Das ist robuster, weil sich Daten zwischen Planung und Ausführung ändern können. Außerdem vermeidest du Probleme mit Größenlimits und Serialisierung.
Als Entscheidungsregel kannst du dir merken: Nutze OneTimeWorkRequest, wenn eine konkrete Aufgabe aus einem Ereignis entsteht und abgeschlossen werden soll. Nutze PeriodicWorkRequest, wenn deine App in größeren Abständen wiederkehrende Wartungs- oder Sync-Arbeit erledigen soll. Setze Constraints so streng wie nötig, aber nicht strenger. Wenn du „nur im WLAN und nur beim Laden“ verlangst, kann die Arbeit lange warten. Wenn mobile Daten fachlich akzeptabel sind, reicht oft NetworkType.CONNECTED.
Für Tests und Debugging solltest du nicht nur prüfen, ob der Code kompiliert. Prüfe, ob die Arbeit wirklich eingeplant wird, ob eindeutige Namen Mehrfachstarts verhindern und ob dein Worker bei Fehlern das passende Result zurückgibt. In Code-Reviews lohnt sich eine feste Checkliste: Ist der Worker klein? Sind Constraints begründet? Ist die Arbeit wiederholbar? Werden Daten aus einer stabilen Quelle geladen? Gibt es eine klare Entscheidung zwischen einmalig und periodisch?
Fazit
Work Requests sind dein Werkzeug, um Hintergrundarbeit in Android klar zu beschreiben: OneTimeWorkRequest für einzelne Aufträge, PeriodicWorkRequest für wiederkehrende Aufgaben und Scheduling über Bedingungen statt direkter Kontrolle. Übe das Thema, indem du in einer kleinen App einen einmaligen Sync einplanst, absichtlich Netzwerkfehler simulierst und im Debugger prüfst, welches Result dein Worker zurückgibt. Danach lässt du den Code reviewen oder schreibst Tests für die Scheduling-Entscheidung, damit du nicht nur die API kennst, sondern auch robuste Hintergrundarbeit entwirfst.