Lang laufende Arbeit in Android
Lang laufende Arbeit braucht sichtbare Nutzerführung. Du lernst, wann Foreground Services und Notifications passen.
Lang laufende Arbeit in Android ist nicht nur eine technische Frage, sondern auch eine Frage der Erwartung: Wenn deine App länger rechnet, lädt, synchronisiert oder aufzeichnet, muss der Nutzer verstehen, was passiert und warum die App weiter aktiv bleibt.
Was ist das?
Long-Running Work bezeichnet Aufgaben, die deutlich länger laufen als ein kurzer UI-Klick oder eine einzelne Datenbankabfrage. Typische Beispiele sind ein Datei-Upload, eine Standortaufzeichnung, eine Audioaufnahme, eine aktive Navigation oder eine größere Synchronisierung, die der Nutzer bewusst gestartet hat. Der wichtige Punkt ist nicht nur die Dauer, sondern die Sichtbarkeit: Die Aufgabe hat für den Nutzer einen aktuellen Wert, und Android erwartet, dass deine App damit offen umgeht.
Im modernen Android-Kontext bedeutet das: Du trennst UI, Fachlogik und Ausführung sauber, nutzt Kotlin Coroutines für strukturierte nebenläufige Arbeit und beachtest trotzdem die Grenzen des Systems. Eine Coroutine in einem ViewModel ist gut für Arbeit, die an den Bildschirm oder die aktuelle Interaktion gebunden ist. Sie ist aber keine Garantie, dass Arbeit weiterläuft, wenn die App in den Hintergrund geht oder der Prozess beendet wird.
Wenn die Aufgabe aktiv weiterlaufen muss und der Nutzer sie wahrnehmen soll, kommt ein Foreground Service ins Spiel. Dieser Service läuft mit erhöhter Sichtbarkeit und muss eine Notification anzeigen. Genau diese Notification ist kein störendes Beiwerk, sondern ein Teil des Plattformvertrags: Deine App beansprucht Ressourcen im Hintergrund, also muss der Nutzer informiert bleiben und möglichst Kontrolle behalten.
Wie funktioniert es?
Das mentale Modell ist einfach: Android unterscheidet zwischen kurzer Arbeit, geplanter Hintergrundarbeit und aktiver, nutzer sichtbarer Langläufer-Arbeit. Kurze Arbeit gehört oft in einen Coroutine-Scope, zum Beispiel in viewModelScope. Geplante oder aufschiebbare Arbeit passt eher zu WorkManager. Arbeit, die jetzt läuft, länger dauern kann und für den Nutzer sichtbar bleiben muss, ist ein Kandidat für einen Foreground Service mit Notification.
Ein Foreground Service wird gestartet, führt seine Aufgabe aus und ruft sehr früh startForeground(...) mit einer Notification auf. Diese Notification zeigt dem Nutzer, dass etwas aktiv passiert. Sie sollte nicht vage sein. Besser ist ein konkreter Text wie „Upload läuft“ oder „Route wird aufgezeichnet“. Je nach Aufgabe kann sie auch Fortschritt, Abbrechen-Aktion oder eine Rückkehr zur passenden App-Oberfläche anbieten.
Coroutines helfen dir, die eigentliche Arbeit im Service lesbar zu strukturieren. Du kannst zum Beispiel einen CoroutineScope im Service verwenden, die Arbeit auf Dispatchers.IO ausführen und den Scope in onDestroy() abbrechen. Flow kann Statusänderungen liefern, etwa Fortschritt beim Upload oder Updates einer laufenden Messung. Wichtig bleibt: Coroutines regeln Nebenläufigkeit in deinem Code, aber sie ändern nicht die Android-Regeln für Hintergrundausführung, Berechtigungen und Sichtbarkeit.
Für Compose ist die Verbindung meist indirekt. Deine Oberfläche startet nicht selbst lange Arbeit in einer Composable-Funktion. Stattdessen löst sie über ein ViewModel, einen Use Case oder eine klare App-Schicht den Start aus. Compose zeigt Zustand an, etwa „Upload vorbereitet“, „läuft“ oder „abgebrochen“. Die Aufgabe selbst gehört nicht in den Recomposition-Zyklus. So vermeidest du, dass UI-Lebenszyklus und Arbeits-Lebenszyklus unklar ineinanderlaufen.
In der Praxis
Stell dir vor, deine App lädt ein großes Video hoch, nachdem der Nutzer bewusst auf „Hochladen“ getippt hat. Solange der Upload nur wenige Sekunden dauert und die App im Vordergrund bleibt, kann eine Coroutine im ViewModel reichen. Wenn der Upload aber mehrere Minuten dauern kann und auch beim Wechsel in eine andere App weiterlaufen soll, brauchst du eine sichtbare Lösung. Dann ist ein Foreground Service sinnvoll, der eine Notification zeigt und den Nutzer nicht im Unklaren lässt.
Eine stark vereinfachte Struktur kann so aussehen:
class UploadService : Service() {
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = buildUploadNotification(progress = 0)
startForeground(UPLOAD_NOTIFICATION_ID, notification)
serviceScope.launch {
try {
uploadRepository.uploadVideo()
stopSelf()
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
showUploadFailedNotification()
stopSelf()
}
}
return START_NOT_STICKY
}
override fun onDestroy() {
serviceScope.cancel()
super.onDestroy()
}
override fun onBind(intent: Intent?): IBinder? = null
}
Das Beispiel zeigt die Grundidee, nicht alle Details einer produktiven Implementierung. In einer echten App brauchst du zusätzlich einen Notification Channel, passende Berechtigungen je nach Android-Version, eine sinnvolle Abbrechen-Aktion und eine klare Fehlerbehandlung. Außerdem solltest du prüfen, ob dein konkreter Anwendungsfall wirklich einen Foreground Service rechtfertigt.
Eine praktische Entscheidungsregel lautet: Wenn der Nutzer die Aufgabe bewusst gestartet hat, sie direkt relevant bleibt und sie auch außerhalb der sichtbaren Activity weiterlaufen soll, denke an einen Foreground Service. Wenn die Arbeit später erledigt werden darf, nicht ständig sichtbar sein muss oder vom System optimiert geplant werden kann, prüfe zuerst geplante Hintergrundarbeit. Wenn die Arbeit nur an den aktuellen Screen gebunden ist, bleibe bei viewModelScope.
Eine typische Stolperfalle ist „unsichtbare Dauerarbeit“. Du startest im Repository eine Coroutine, die lange läuft, und hoffst, dass sie schon durchkommt. Das wirkt im Test oft stabil, bricht aber in echten Situationen: Der Nutzer verlässt die App, Android spart Energie, der Prozess wird beendet oder die Notification fehlt. Eine zweite Stolperfalle ist eine Notification ohne Nutzen. Wenn dort nur „App läuft“ steht, versteht der Nutzer nicht, welche Aufgabe aktiv ist. Gute User Awareness heißt: klarer Zweck, verständlicher Status und, wo sinnvoll, Kontrolle.
In Code-Reviews solltest du bei Long-Running Work gezielt nach drei Fragen suchen: Ist der Lebenszyklus der Arbeit klar? Sieht der Nutzer, dass die Aufgabe läuft? Wird die Arbeit bei Abbruch, Fehler oder App-Ende sauber beendet? Diese Fragen sind oft wertvoller als die Suche nach der neuesten API-Variante, weil sie die Architektur und das Nutzererlebnis gleichzeitig prüfen.
Fazit
Lang laufende Arbeit ist Android-Entwicklung an der Grenze zwischen Code, Systemregeln und Nutzervertrauen. Nutze Coroutines und Flow, um die Arbeit sauber zu strukturieren, aber entscheide bewusst, ob ein Foreground Service mit Notification nötig ist. Als Übung kannst du einen bestehenden Upload, Timer oder Recorder in deinem Projekt prüfen: Verlasse während der Arbeit die App, beobachte Logs und Notification, teste Abbruch und Fehlerfall, und lass im Code-Review besonders Lebenszyklus, Sichtbarkeit und Nutzerkontrolle bewerten.