SideEffect — was ist das, Zustandssynchronisation in Jetpack Compose

Autor: IT Sectr Veröffentlicht: 2026-06-30 Lesezeit: 9 Min.

SideEffect ist eine composable-Funktion in Jetpack Compose, die den übergebenen Codeblock bei jeder erfolgreichen Rekomposition ausführt. Anders als LaunchedEffect und DisposableEffect ist SideEffect nicht an Schlüssel gebunden und hat keinen Bereinigungsblock — es synchronisiert einfach den Compose-Zustand nach jedem Rendern mit externen Systemen. Dies macht es ideal zum Aktualisieren von Callback-Funktionen, zur Synchronisation mit ViewPager und zur Datenübergabe an Analytics SDK. Laut Android Developers Documentation (2025) wird SideEffect erst ausgeführt, nachdem Compose eine erfolgreiche Rekomposition bestätigt hat, und wird nicht ausgeführt, wenn die Rekomposition übersprungen wurde.

Wichtige Punkte

  • SideEffect — Side-Effect-API für Code, der nach jeder erfolgreichen Rekomposition ausgeführt wird.
  • Synchronisation — übergibt den Compose-Zustand an externe Systeme, die Compose nicht unterstützen.
  • Keine Schlüssel — anders als LaunchedEffect wird SideEffect nicht neu gestartet, sondern bei jeder Rekomposition ausgeführt.
  • Keine Bereinigung — SideEffect bietet kein onDispose, es ist nur für unidirektionale Synchronisation ausgelegt.
  • Synchron — der Block wird synchron innerhalb der Compose-Kompositionsphase ausgeführt, ohne Koroutinen.

Was ist SideEffect in Jetpack Compose

SideEffect ist die einfachste der Side-Effect-APIs in Jetpack Compose. Es führt einen Codeblock bei jeder erfolgreichen Rekomposition einer composable-Komponente aus. Das Wort „erfolgreich“ ist hier der Schlüssel: wenn Compose entscheidet, dass keine Rekomposition erforderlich ist (z. B. alle Eingabeparameter sind unverändert und das Ergebnis bleibt gleich), wird SideEffect nicht ausgeführt. Dadurch wird sichergestellt, dass der Synchronisationsblock nur aufgerufen wird, wenn sich die Benutzeroberfläche tatsächlich geändert hat.

Der Hauptanwendungsfall von SideEffect ist die Synchronisation des Compose-Zustands mit Objekten, die nicht Teil des Compose-Baums sind. Typische Beispiele sind: Aktualisieren einer Callback-Funktion in einem Legacy-View-System, Übergeben des aktuellen Zustands an ViewPager, Senden eines Ereignisses an ein Analyse-SDK bei Änderung der angezeigten Daten und Synchronisation mit Kartierungs-SDKs, die Aktualisierungen in einem externen Format erwarten.

Laut Android Developer Blog (2025) wird SideEffect oft in Verbindung mit remember verwendet: remember bewahrt ein Objekt (z. B. einen Callback) auf, und SideEffect aktualisiert es bei jeder Änderung einer Abhängigkeit. Dieses Muster ist besonders wichtig für Bibliotheken, die Listener-Objekte akzeptieren und sie bei Aktualisierungen nicht neu erstellen — ohne SideEffect würde der Listener eine veraltete Referenz auf den aktuellen Zustand enthalten.

kotlin
@Composable
fun MapScreen(zoomLevel: Int, markers: List<Marker>) {
    val mapView = remember { MapView(LocalContext.current) }
    
    SideEffect {
        mapView.setZoom(zoomLevel)
        mapView.updateMarkers(markers)
    }
    
    AndroidView(factory = { mapView })
}

Wie SideEffect funktioniert und die Kompositionsphasen

Um SideEffect zu verstehen, müssen Sie die Ausführungsphasen von Jetpack Compose verstehen. Compose durchläuft drei Phasen für jeden Frame: Composition (was anzuzeigen ist), Layout (wo anzuzeigen ist), Drawing (wie anzuzeigen ist). SideEffect wird am Ende der Composition-Phase ausgeführt — nachdem alle composable-Funktionen gelaufen sind, aber vor der Layout-Phase. Dadurch wird sichergestellt, dass SideEffect den endgültigen Zustand aller Variablen nach der Rekomposition sieht.

Diese Position im Lebenszyklus bietet einen wichtigen Vorteil: SideEffect kann keine endlose Rekomposition verursachen, selbst wenn der Zustand darin geändert wird. Da es nach der Komposition ausgeführt wird, werden Änderungen innerhalb von SideEffect erst im nächsten Frame angewendet — dies verhindert die typischen Zyklen von Zustandsänderungen innerhalb des Körpers einer composable-Funktion (wenn setState innerhalb der Komposition eine neue Komposition auslöst, bevor die aktuelle abgeschlossen ist).

Ein weiteres Merkmal ist, dass SideEffect nicht nach Schlüsseln optimiert wird. Es wird bei jeder Rekomposition ausgeführt, unabhängig davon, welcher spezifische Zustand sich geändert hat. Wenn Sie eine genauere Kontrolle benötigen (nur ausführen, wenn sich ein bestimmter Parameter ändert), verwenden Sie LaunchedEffect mit Schlüsseln oder umschließen Sie SideEffect mit einer Änderungsprüfung über remember.

kotlin
// SideEffect optimized with remember
var currentZoom by remember { mutableIntStateOf(zoomLevel) }

SideEffect {
    if (currentZoom != zoomLevel) {
        map.animateToZoom(zoomLevel)
        currentZoom = zoomLevel
    }
}

// Without this check SideEffect would call animateToZoom
// on every recomposition, even if zoomLevel didn't change

Aktualisierung von Callback-Funktionen mit SideEffect

Das häufigste praktische Szenario für SideEffect ist die Aktualisierung von Callback-Funktionen, die den aktuellen Zustand erfassen. In Jetpack Compose wird dies als „Callback-Lebenszyklusverwaltung“ bezeichnet. Das Problem ist, dass Lambda-Ausdrücke in Kotlin Variablen per Referenz erfassen, und wenn ein Callback mit einem Wert erstellt wurde und sich die Variable später ändert — verwendet der Callback weiterhin den alten Wert.

Betrachten Sie ein Beispiel: Das Google Maps SDK für Android akzeptiert ein OnCameraMoveListener-Objekt über setOnCameraMoveListener(). Wenn Sie ein Lambda übergeben, das isTrackingEnabled erfasst, wird das Lambda bei einer Änderung von isTrackingEnabled nicht aktualisiert — das Maps SDK ruft weiterhin den alten Callback mit veralteten Daten auf. SideEffect löst dieses Problem: es setzt den Listener bei jeder Rekomposition neu, wodurch sichergestellt wird, dass das SDK immer das aktuelle Lambda mit dem neuesten Zustand verwendet.

Laut Maps SDK for Android Dokumentation (2025) empfiehlt Google genau dieses Muster bei der Integration von Maps mit Jetpack Compose. Ein ähnlicher Ansatz wird für WebView, VideoView, TextureView und alle anderen View-basierten Komponenten verwendet, die Callbacks über set-Methoden akzeptieren. SideEffect stellt sicher, dass die Callbacks bei jeder Zustandsänderung auf dem neuesten Stand sind.

kotlin
@Composable
fun MapComposable(isTrackingEnabled: Boolean, onMarkerClick: (Marker) -> Unit) {
    val mapView = remember { MapView(LocalContext.current) }
    
    SideEffect {
        mapView.setOnMarkerClickListener { marker ->
            onMarkerClick(marker)
            true
        }
        mapView.isTrafficEnabled = isTrackingEnabled
    }
    
    AndroidView(factory = { mapView })
}

Synchronisation mit Analytics SDK

Ein weiteres wichtiges Szenario für SideEffect ist das Senden von Ereignissen an Analysesysteme bei Änderung des UI-Zustands. Wenn ein Benutzer beispielsweise in einem TabLayout innerhalb eines Compose-Bildschirms Registerkarten wechselt, kann SideEffect den aktuellen Zustand der ausgewählten Registerkarte an Firebase Analytics oder AppsFlyer übergeben. Jedes Mal, wenn sich die ausgewählte Registerkarte ändert (und eine Rekomposition erfolgt), sendet SideEffect das entsprechende Ereignis.

Der Unterschied zum direkten Senden von Ereignissen in onClick oder onTabSelected besteht darin, dass SideEffect bei Zustandsänderungen aus jeder Quelle ausgelöst wird — nicht nur bei Benutzeraktionen, sondern auch bei programmatischen Änderungen, Zustandswiederherstellung nach Bildschirmdrehung oder Deep Links. Dies macht SideEffect zu einem universellen Synchronisationsmechanismus, der unabhängig von der Änderungsquelle ist.

Laut Firebase Best Practices (Google, 2025) liefert das Senden von Analyse-Ereignissen über SideEffect ein vollständigeres Bild der Benutzerreise, da es alle Zustandsänderungen erfasst, einschließlich solcher, die ohne direkte Benutzeraktion auftreten. Es ist jedoch wichtig, es nicht zu übertreiben: jedes Analyse-Ereignis ist eine Netzwerkanfrage, daher ist SideEffect für häufig wechselnde Zustände (Bildlaufposition, Fingerkoordinaten) nicht geeignet — verwenden Sie Debounce oder senden Sie Ereignisse nur bei signifikanten Änderungen.

kotlin
@Composable
fun ProductScreen(selectedTab: ProductTab, productId: String) {
    val firebaseAnalytics = remember { FirebaseAnalytics.getInstance(LocalContext.current) }
    
    SideEffect {
        val params = Bundle().apply {
            putString(FirebaseAnalytics.Param.CONTENT_TYPE, selectedTab.name)
            putString(FirebaseAnalytics.Param.ITEM_ID, productId)
        }
        firebaseAnalytics.logEvent(        FirebaseAnalytics.Event.VIEW_ITEM, params)
    }
    
    // UI with TabRow and selected tab
}

SideEffect vs LaunchedEffect: wann was verwenden

Die Wahl zwischen SideEffect und LaunchedEffect hängt von zwei Faktoren ab: ob asynchrone Ausführung benötigt wird und ob schlüsselbasierte Steuerung benötigt wird. SideEffect ist synchron und wird bei jeder Rekomposition ausgeführt. LaunchedEffect ist asynchron (Koroutine) und wird nur ausgeführt, wenn sich ein Schlüssel ändert, nicht bei jeder Rekomposition.

EigenschaftSideEffectLaunchedEffect
AusführungBei jeder RekompositionBei Schlüsseländerung
AsynchronitätSynchronKoroutine
SchlüsselNeinJa (vararg)
BereinigungNeinAutomatische Koroutinen-Abbrechung
Typische VerwendungCallbacks, Analytics, View-SynchronisationLaden, Flow-Abonnement, Timer

In der Praxis werden 70% der Anwendungsfälle von Seiteneffekten durch LaunchedEffect (asynchrone Operationen, Daten laden), 20% durch DisposableEffect (Ressourcen mit Bereinigung) und nur 10% durch SideEffect (Callback-Synchronisation) abgedeckt. SideEffect ist ein spezialisiertes Werkzeug für einen begrenzten Aufgabenbereich, aber in diesen Aufgaben ist es unverzichtbar.

Häufige Fehler mit SideEffect

Der Hauptfehler ist das Ändern des Compose-Zustands innerhalb von SideEffect. Obwohl SideEffect nicht direkt eine Endlosschleife verursacht (da es nach der Kompositionsphase ausgeführt wird), kann es übermäßige Rekompositionen auslösen. Wenn der Zustand innerhalb von SideEffect geändert wird (mutableStateOf), löst dies eine neue Rekomposition im nächsten Frame aus, die wiederum SideEffect ausführt — und so weiter bis zur Stabilisierung. Dies ist keine Endlosschleife, aber unnötige Arbeit für das Framework.

Der zweite Fehler ist das Ausführen schwerer Berechnungen innerhalb von SideEffect. Da SideEffect bei jeder Rekomposition aufgerufen wird und Rekompositionen dutzende Male pro Sekunde auftreten können (bei Animationen, Scrollen), führt jeder schwere Code innerhalb von SideEffect zu Frame-Einbrüchen. Verlagern Sie schwere Operationen aus der Komposition — in eine Koroutine (LaunchedEffect) oder berechnen Sie über derivedStateOf / remember.

Der dritte Fehler ist der Versuch, SideEffect für asynchronen Code zu verwenden. SideEffect ist keine suspend-Funktion, daher werden delay(), await(), collect() und andere Koroutinenoperationen darin nicht kompiliert. Wenn Sie eine asynchrone Aktion nach der Rekomposition ausführen müssen, verwenden Sie snapshotFlow { ... } in Kombination mit LaunchedEffect oder starten Sie eine Koroutine über rememberCoroutineScope.

Häufig gestellte Fragen

Wird SideEffect bei der ersten Komposition ausgeführt?

Ja, SideEffect wird bei jeder erfolgreichen Komposition ausgeführt, einschließlich der ersten — wenn die Komponente zum ersten Mal auf dem Bildschirm erscheint. Dies unterscheidet es von LaunchedEffect(Unit), das ebenfalls einmal bei der ersten Komposition ausgeführt wird, aber nicht bei nachfolgenden Rekompositionen (wenn sich der Schlüssel nicht geändert hat).

Kann SideEffect eine Endlosschleife verursachen?

Nein, SideEffect wird nach der Kompositionsphase ausgeführt — Änderungen innerhalb werden erst im nächsten Frame angewendet, was Schleifen verhindert. Häufiges Ändern des Zustands innerhalb von SideEffect kann jedoch eine Kaskade von Rekompositionen auslösen, die die Leistung beeinträchtigt. Ändern Sie den Zustand innerhalb von SideEffect nur, wenn es wirklich notwendig ist.

Was ist der Unterschied zwischen SideEffect und snapshotFlow?

SideEffect wird bei jeder Rekomposition synchron ausgeführt. snapshotFlow erstellt einen Flow aus dem Compose-Zustand und kann mit collectLatest in LaunchedEffect für reaktive Änderungsverarbeitung verwendet werden. snapshotFlow eignet sich für Fälle, in denen Sie mit debounce, filter oder distinctUntilChanged auf Änderungen reagieren müssen — was im synchronen SideEffect unmöglich ist.

Wie debugge ich SideEffect, wenn es zu häufig ausgeführt wird?

Verwenden Sie den Android Studio Compose Modifier Debugger oder fügen Sie eine Protokollierung mit dem Komponentennamen und der Aufrufhäufigkeit hinzu. Wenn SideEffect häufiger als erwartet ausgeführt wird, überprüfen Sie, ob sich der Zustand der übergeordneten Komponente unnötig ändert. Optimierung: extrahieren Sie stabile Teile der Benutzeroberfläche in separate composable-Funktionen mit unstable-Annotationen, um die Anzahl der Rekompositionen zu reduzieren.

Kann SideEffect mit DisposableEffect kombiniert werden?

Ja, sie können in derselben Komponente für unterschiedliche Zwecke verwendet werden. DisposableEffect ist für die Einrichtung und Bereinigung einer Ressource (einmal) zuständig, während SideEffect die Synchronisation des aktuellen Zustands mit dieser Ressource bei jeder Rekomposition übernimmt. Ein typisches Beispiel: DisposableEffect registriert einen Callback über eine API, und SideEffect aktualisiert die erfassten Daten in diesem Callback bei jeder Änderung.

Zusammenfassung

  • SideEffect — Side-Effect-API für synchronen Code, der nach jeder erfolgreichen Rekomposition in Jetpack Compose ausgeführt wird.
  • Synchronisation — der Hauptanwendungsfall: Übergabe des Compose-Zustands an externe Systeme (Google Maps, WebView, ViewPager, Analytics SDK).
  • Callbacks — SideEffect stellt sicher, dass Callback-Funktionen, die den aktuellen Zustand erfassen, bei jeder UI-Aktualisierung auf dem neuesten Stand bleiben.
  • Keine Schleifen — wird nach der Kompositionsphase ausgeführt, daher verursacht das Ändern des Zustands innerhalb von SideEffect keine endlose Rekomposition.
  • Einschränkungen — unterstützt keine Schlüssel, Asynchronität oder Bereinigungsblock; für diese Aufgaben verwenden Sie LaunchedEffect oder DisposableEffect.
  • Leistung — vermeiden Sie schwere Berechnungen innerhalb von SideEffect, da es bei jeder Rekomposition ausgeführt wird (bis zu 60 Mal pro Sekunde).
  • Debugging — kontrollieren Sie die Aufrufhäufigkeit über den Compose Debugger und optimieren Sie mit remember, um unnötige Rekompositionen zu filtern.

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch