Jetpack Compose Performance: Warum deine App langsam ist
Jetpack Compose verändert die Art, wie Android-Apps entwickelt werden: Schnelleres Entwickeln, weniger Code und moderne, deklarative UI. Doch Vorsicht – selbst kleine Fehler im Code können schnell zu erheblichen Performance-Einbußen führen.
Ein besonders häufiger Fallstrick ist die Nutzung von Default-Parametern in Composables. Hier erfährst du, wie du typische Jetpack Compose Performance-Probleme erkennst und beseitigst, um deine Jetpack Compose Performance deutlich zu verbessern.
Das Kernproblem: Default-Parameter und unnötige Recompositions
Compose erstellt UIs durch regelmäßige Recompositions neu. Dabei prüft Compose, ob sich Eingabeparameter geändert haben. Leider kann dies bei falscher Nutzung von Default-Parametern unnötig oft passieren.
Warum neue Instanzen ein Problem für die Performance sind
Jede neue Instanz von Parametern führt potenziell zu einer zusätzlichen Recomposition – selbst wenn sich inhaltlich nichts geändert hat. Besonders kritisch wird es in scrollbaren Listen, wo hunderte Objekte pro Sekunde erzeugt werden. Das Resultat: Eine langsame, ruckelnde App.
Mehr zur Recomposition bei Android Developers →
Die versteckte Gefahr: Factory-Pattern und Default-Parameter
Libraries wie Material 3 verwenden häufig das Factory-Pattern. Obwohl Factory-Patterns bequem und sauber wirken, erzeugen sie oft bei jedem Aufruf ein neues Objekt.
Ein Beispiel ist die bekannte copy()
-Methode, die zwar flexibel, aber nicht besonders performant ist, wenn sie direkt in Default-Parametern verwendet wird.
Drei Lösungen für optimale Jetpack Compose Performance
Hier findest du drei praxiserprobte Lösungen, mit denen du stabile und effiziente Default-Parameter definierst.
1. Stabile Werte mit CompositionLocal (Empfohlen)
CompositionLocals ermöglichen, Werte global zu definieren und effizient weiterzugeben. Sie verhindern das ständige Neuerzeugen von Objekten und sorgen für stabilere Recompositions:
- Globale Instanz einmal definieren
- Standardmäßig überall verwenden
- Flexibel überschreibbar, ähnlich wie Themes
Ein einfaches Beispiel:
val LocalDefaultColor = compositionLocalOf { Color(0xFFFF0000) }
@Composable
fun MyComposable(color: Color = LocalDefaultColor.current) { ... }
CompositionLocals sind besonders gut für Standardwerte geeignet, die global einheitlich sein sollen.
2. Unveränderliche Werte als statische Objekte definieren
Falls deine Werte sich zur Laufzeit nie ändern, sind statische Objekte ideal:
- Nur einmal erzeugt
- Keine unnötige Recomposition
- Maximale Performance
Nachteil: Diese Werte sind fix – ideal, wenn dynamische Anpassung nicht nötig ist.
Beispiel:
object DefaultConfig {
val color = Color(0xFFFF0000)
val size = 12.dp
}
Diese Methode eignet sich gut für feste Layouts oder globale Designrichtlinien.
3. Dynamische Werte effizient mit Remember verwalten
Brauchst du Flexibilität und dynamische Anpassungen während der Laufzeit, dann nutze den Remember-Block:
@Composable
fun MyComposable(dynamicValue: Int) {
val config = remember(dynamicValue) {
createConfig(dynamicValue)
}
// Verwende config hier weiter
}
Remember erzeugt dein Objekt nur, wenn sich die angegebenen Parameter ändern, und vermeidet unnötige Recompositions.
Typische Fehler und bessere Alternativen
❌ Fehler 1: Default-Werte direkt instanziieren
Schlecht:
@Composable
fun MyComposable(color: Color = Color(0xFFFF0000)) { ... }
Besser mit CompositionLocal:
val LocalDefaultColor = compositionLocalOf { Color(0xFFFF0000) }
@Composable
fun MyComposable(color: Color = LocalDefaultColor.current) { ... }
❌ Fehler 2: Factory-Methoden direkt in Default-Parametern
Schlecht:
@Composable
fun MyComposable(config: Config = createDefaultConfig()) { ... }
Besser:
val LocalDefaultConfig = compositionLocalOf { createDefaultConfig() }
@Composable
fun MyComposable(config: Config = LocalDefaultConfig.current) { ... }
❌ Fehler 3: Dynamische Werte ohne Remember erzeugen
Schlecht:
@Composable
fun MyComposable(dynamicValue: Int) {
// bei jeder Recomposition neu erzeugt!
val config = createConfig(dynamicValue)
}
Besser:
@Composable
fun MyComposable(dynamicValue: Int) {
val config = remember(dynamicValue) { createConfig(dynamicValue) }
}
Jetpack Compose Performance messen und analysieren
Um konkrete Performance-Verbesserungen nachzuweisen, solltest du Tools wie den Compose Layout Inspector oder Perfetto nutzen. Diese Tools helfen dir dabei, unnötige Recompositions gezielt zu entdecken und zu optimieren.
Fazit: Kleine Änderungen, große Wirkung für deine Jetpack Compose Performance
Du kannst die Performance deiner Compose-App maßgeblich steigern, indem du:
- Standardwerte über CompositionLocals definierst
- Statische Objekte für unveränderliche Werte nutzt
- Dynamische Instanzen gezielt mit Remember erzeugst
Vermeide unnötige Recompositions und erhöhe spürbar die Reaktionsgeschwindigkeit deiner Apps. So wird dein Jetpack Compose Code nicht nur elegant, sondern auch performant.
Hast du weitere Fragen oder eigene Erfahrungen mit diesen Problemen? Teile sie gern in den Kommentaren!
Weitere nützliche Quellen:
Für regelmäßige Praxistipps rund um Android und Jetpack Compose, folge diesem Blog und bleibe stets informiert!