Activity Lifecycle in Android: Zustände, Callbacks und sicheres State-Handling
Verstehe den Activity-Lifecycle in Android: Callbacks, Zustände und wie du State über Konfigurationsänderungen rettest.
Wenn du eine Android-App entwickelst, hast du es ständig mit einer Activity zu tun, auch wenn du primär in Compose arbeitest. Eine Activity ist der Einstiegspunkt für eine sichtbare Oberfläche, und das System entscheidet, wann sie startet, in den Hintergrund wandert oder zerstört wird. Genau dieses Kommen und Gehen wird über den Activity-Lifecycle gesteuert. Wer den Lifecycle versteht, vermeidet typische Anfängerfehler wie verlorene Eingaben nach einer Drehung, Memory-Leaks durch Listener oder Abstürze, weil ein Callback zur falschen Zeit auf die UI zugreift. In diesem Artikel bekommst du ein klares mentales Modell, lernst die wichtigsten Callbacks kennen und siehst, wie du State sicher durch Zustandswechsel rettest.
Was ist das?
Der Activity-Lifecycle ist die Abfolge an Zuständen, die eine Activity während ihrer Lebensdauer durchläuft. Du kannst ihn dir wie eine Treppe vorstellen, die deine Activity hinauf- und hinuntergeht: Sie wird erzeugt, sichtbar, interaktiv, verliert den Fokus, verschwindet aus dem Bildschirm und wird schließlich zerstört. Android steuert diesen Ablauf, nicht du. Dein Code reagiert nur über sogenannte Lifecycle-Callbacks, also Methoden, die das System zum richtigen Zeitpunkt aufruft.
Die vier Hauptzustände, die du kennen musst, heißen Created, Started, Resumed und Destroyed. Im Zustand Created existiert die Activity im Speicher, ist aber noch nicht sichtbar. Im Zustand Started ist sie für den Nutzer sichtbar, jedoch nicht zwingend im Vordergrund. Im Zustand Resumed läuft sie im Vordergrund und nimmt Eingaben entgegen. Im Zustand Destroyed wird sie aus dem Speicher entfernt, etwa weil der Nutzer sie geschlossen hat oder das System Speicher freigeben muss. Zwischen Started und Stopped gibt es noch den Zustand Paused, der oft auftritt, wenn ein Dialog oder eine andere Activity teilweise über deiner Activity liegt.
Wichtig ist die Einordnung in modernes Android: Auch wenn du deine UI komplett mit Jetpack Compose baust, wird Compose innerhalb einer Activity gehostet. Der Lifecycle der Activity bleibt also relevant, weil er bestimmt, wann deine Composables überhaupt rekomponiert werden, wann ViewModels überleben und wann State verloren geht. Compose und Lifecycle arbeiten zusammen, sie ersetzen sich nicht.
Wie funktioniert es?
Wenn der Nutzer deine App öffnet, ruft Android nacheinander mehrere Methoden deiner Activity auf. Zuerst kommt onCreate, dann onStart, danach onResume. Ab onResume ist deine Activity vollständig sichtbar und reagiert auf Touches. Verlässt der Nutzer die App oder öffnet einen Dialog, wandert die Activity zurück: Es laufen onPause, anschließend onStop und im Zerstörungsfall schließlich onDestroy. Kommt der Nutzer zurück, durchläuft die Activity wieder onStart und onResume. Diese Symmetrie ist kein Zufall, sondern dein wichtigstes Werkzeug, um Ressourcen sauber zu erwerben und freizugeben.
Jeder Callback hat eine klare Aufgabe. In onCreate initialisierst du Dinge, die genau einmal pro Activity-Instanz passieren sollen, etwa das Setzen des Compose-Contents über setContent oder das Verbinden mit einem ViewModel. In onStart und onStop koppelst du Komponenten an UI-relevante Quellen an und ab, zum Beispiel Sensoren oder Standortdaten, die nur Sinn ergeben, solange die Activity sichtbar ist. In onResume und onPause reagierst du auf den Fokus, etwa um Animationen zu starten oder Eingaben zu pausieren. In onDestroy räumst du letzte Referenzen auf, falls sie nicht ohnehin durch Lifecycle-aware Komponenten verwaltet werden.
Ein zentraler Punkt ist, dass eine Activity während ihrer Lebenszeit mehrfach zerstört und neu erzeugt werden kann, ohne dass die App geschlossen wird. Der häufigste Auslöser ist eine Konfigurationsänderung wie das Drehen des Geräts, ein Theme-Wechsel zwischen Hell- und Dunkelmodus oder ein Sprachwechsel. In diesen Fällen ruft Android onDestroy auf und erzeugt sofort eine neue Activity-Instanz mit onCreate. Alles, was nur als gewöhnliches Property in der Activity-Klasse lag, ist danach weg. Genau deshalb gilt: State, der für den Nutzer wichtig ist, darf nicht ausschließlich in der Activity selbst leben.
Der Lifecycle in Compose
In Jetpack Compose hast du es zusätzlich mit dem Lebenszyklus von Composables zu tun, der innerhalb der Activity stattfindet. Ein Composable wird komponiert, gegebenenfalls rekomponiert und schließlich verworfen, wenn es nicht mehr benötigt wird. State, den du mit remember deklarierst, überlebt Rekompositionen, aber nicht die Zerstörung der Activity. Möchtest du, dass ein Wert auch eine Drehung übersteht, nutzt du rememberSaveable. Möchtest du Daten an die Activity-Lebensdauer hinaus erhalten, nutzt du ein ViewModel. Diese Unterscheidung ist die wichtigste Brücke zwischen klassischem Lifecycle-Wissen und Compose-Praxis.
Lifecycle-aware Komponenten
Statt jeden Callback einzeln zu überschreiben, arbeitest du heute meist mit Lifecycle-aware Komponenten. Ein ViewModel überlebt Konfigurationsänderungen automatisch und gibt seinen State an die neu erzeugte Activity zurück. Ein LifecycleObserver reagiert auf Lifecycle-Events, ohne dass du selbst auf jede Methode achten musst. In Compose stellt dir LifecycleEventEffect oder DisposableEffect saubere Hooks bereit, um Aktionen genau dann auszulösen, wenn die Activity in einen bestimmten Zustand wechselt. Dadurch bleibt dein Code lesbar und du vermeidest die typische Callback-Suppe der frühen Android-Tage.
In der Praxis
Schauen wir uns ein konkretes Beispiel an: ein einfacher Eingabe-Screen, dessen Inhalt eine Bildschirmdrehung überstehen soll. Du brauchst dafür weder XML noch komplizierte Klassen, sondern nutzt Compose zusammen mit rememberSaveable.
class NotesActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
NoteEditor()
}
}
}
@Composable
fun NoteEditor() {
var note by rememberSaveable { mutableStateOf("") }
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Deine Notiz")
TextField(
value = note,
onValueChange = { note = it },
modifier = Modifier.fillMaxWidth()
)
}
}
Was passiert hier konkret? In onCreate setzt du den Compose-Content. Der Composable NoteEditor hält einen String-State über rememberSaveable. Dreht der Nutzer das Gerät, zerstört Android die Activity, ruft onDestroy auf und legt anschließend mit onCreate eine neue Instanz an. rememberSaveable schreibt den Wert vorher in das savedInstanceState-Bundle und stellt ihn nach der Neuerstellung wieder her. Der Nutzer merkt von all dem nichts, seine Notiz bleibt sichtbar.
Für komplexere Daten, etwa Listen aus dem Netzwerk oder berechnete UI-Zustände, hebst du den State zusätzlich in ein ViewModel. Das ViewModel wird vom System an einen sogenannten Scope gebunden, der über die Activity-Neuerstellung hinaus existiert. Dadurch musst du teure Operationen wie Netzwerk-Aufrufe nicht nach jeder Drehung wiederholen.
Eine konkrete Entscheidungsregel hilft dir bei der Auswahl des richtigen Werkzeugs:
- Ist der Wert klein, primitiv und UI-nah, etwa Text in einem Eingabefeld? Nutze
rememberSaveabledirekt im Composable. - Ist der Wert größer, das Ergebnis von Logik oder Netzwerk-Daten? Nutze ein
ViewModel, optional mitSavedStateHandle, wenn du auch Prozesstod überleben willst. - Geht es um Daten, die Nutzer dauerhaft behalten sollen? Nutze eine richtige Persistenzschicht wie Room oder DataStore.
Typische Stolperfalle
Die häufigste Falle für Einsteiger ist, schwere Arbeit in onCreate oder onResume zu legen. Wenn du dort synchron eine Datenbank öffnest, ein großes Bild dekodierst oder einen Netzwerk-Aufruf blockierend wartest, fühlt sich deine App beim Start zäh an, und das System kann deine Activity sogar wegen ANR-Fehlern beenden. onCreate läuft auf dem Main-Thread, genauso wie alle anderen Lifecycle-Callbacks. Schiebe deshalb längere Operationen in Coroutines mit einem passenden Dispatcher, idealerweise gestartet aus einem ViewModel.
Eine zweite Falle: das Vergessen, in onPause oder onStop Listener und Sensoren wieder freizugeben. Wenn du in onResume einen Standort-Listener registrierst, aber nicht in onPause abmeldest, läuft er weiter, obwohl deine Activity nicht mehr sichtbar ist. Das kostet Akku, kann zu Speicherlecks führen und führt zu schwer auffindbaren Bugs. Nutze hier konsequent Lifecycle-aware Komponenten, die das Anmelden und Abmelden für dich übernehmen, statt manuell jeden Pfad abzudecken.
Eine dritte Falle betrifft den sogenannten Prozesstod. Wenn deine App im Hintergrund liegt und das System Speicher braucht, kann es deinen gesamten Prozess beenden. Bei Rückkehr stellt Android die Activity zwar wieder her, aber nur State, der explizit gespeichert wurde, kommt zurück. rememberSaveable und SavedStateHandle decken diesen Fall ab; reine remember-Werte oder normale Properties nicht.
Fazit
Der Activity-Lifecycle ist kein theoretisches Konzept, sondern ein tägliches Werkzeug, das darüber entscheidet, ob sich deine App stabil und schnell anfühlt. Wenn du die vier Hauptzustände Created, Started, Resumed und Destroyed im Kopf hast, jeden Callback nur für seine Aufgabe nutzt und State bewusst über rememberSaveable, ViewModel oder SavedStateHandle rettest, vermeidest du die meisten klassischen Fehler. Prüfe dein Verständnis aktiv: Lege ein kleines Compose-Projekt an, drehe das Gerät, beobachte mit Logging in den Callbacks, was Android tatsächlich aufruft, und schreibe einen einfachen UI-Test, der einen Configuration-Change simuliert. Wenn dein State danach noch da ist und kein Listener doppelt feuert, hast du den Lifecycle wirklich verstanden und kannst ihn in jedem deiner nächsten Android-Projekte gezielt einsetzen.