Auf dieser Seite erfahren Sie mehr über den Lebenszyklus einer zusammensetzbaren Funktion und darüber, wie Compose entscheidet, ob eine zusammensetzbare Funktion neu zusammengesetzt werden muss.
Lebenszyklus – Übersicht
Wie in der Dokumentation zum Verwalten des Status erwähnt, beschreibt eine Composition die Benutzeroberfläche Ihrer App und wird durch Ausführen von Composables erstellt. Eine Komposition ist eine Baumstruktur der zusammensetzbaren Funktionen, die Ihre UI beschreiben.
Wenn Jetpack Compose Ihre zusammensetzbaren Funktionen zum ersten Mal während der ersten Zusammensetzung ausführt, werden die zusammensetzbaren Funktionen, die Sie zum Beschreiben Ihrer UI in einer Zusammensetzung aufrufen, im Auge behalten. Wenn sich der Status Ihrer App ändert, plant Jetpack Compose eine Neuzusammensetzung. Bei der Neuzusammensetzung führt Jetpack Compose die Composables, die sich aufgrund von Statusänderungen geändert haben, noch einmal aus und aktualisiert dann die Zusammensetzung, um alle Änderungen widerzuspiegeln.
Eine Komposition kann nur durch eine anfängliche Komposition erstellt und durch eine Neukomposition aktualisiert werden. Eine Composition kann nur durch eine Neuzusammensetzung geändert werden.
Die Neuzusammensetzung wird normalerweise durch eine Änderung an einem State<T>-Objekt ausgelöst. Compose verfolgt diese und führt alle Composables in der Komposition aus, die diesen bestimmten State<T> lesen, sowie alle von ihnen aufgerufenen Composables, die nicht übersprungen werden können.
Wenn eine zusammensetzbare Funktion mehrmals aufgerufen wird, werden mehrere Instanzen in der Composition platziert. Jeder Aufruf hat einen eigenen Lebenszyklus in der Komposition.
@Composable fun MyComposable() { Column { Text("Hello") Text("World") } }
MyComposable in der Komposition. Wenn eine zusammensetzbare Funktion mehrmals aufgerufen wird, werden mehrere Instanzen in der Komposition platziert. Ein Element mit einer anderen Farbe weist darauf hin, dass es sich um eine separate Instanz handelt.Anatomie einer zusammensetzbaren Funktion in der Komposition
Die Instanz einer zusammensetzbaren Funktion in der Komposition wird durch ihre Aufrufstelle identifiziert. Der Compose-Compiler betrachtet jede Aufrufstelle als eigenständig. Wenn du Composables von mehreren Aufrufstellen aus aufrufst, werden mehrere Instanzen des Composables in der Komposition erstellt.
Wenn eine zusammensetzbare Funktion während einer Neuzusammensetzung andere zusammensetzbare Funktionen aufruft als bei der vorherigen Zusammensetzung, erkennt Compose, welche zusammensetzbaren Funktionen aufgerufen wurden oder nicht. Bei den zusammensetzbaren Funktionen, die in beiden Zusammensetzungen aufgerufen wurden, vermeidet Compose eine Neuzusammensetzung, wenn sich ihre Eingaben nicht geändert haben.
Die Beibehaltung der Identität ist entscheidend, um Nebenwirkungen mit ihrem Compose-Element zu verknüpfen, damit sie erfolgreich abgeschlossen werden können, anstatt bei jeder Neuzusammensetzung neu gestartet zu werden.
Dazu ein Beispiel:
@Composable fun LoginScreen(showError: Boolean) { if (showError) { LoginError() } LoginInput() // This call site affects where LoginInput is placed in Composition } @Composable fun LoginInput() { /* ... */ } @Composable fun LoginError() { /* ... */ }
Im Code-Snippet oben ruft LoginScreen die komponierbare Funktion LoginError bedingt und die komponierbare Funktion LoginInput immer auf. Jeder Aufruf hat eine eindeutige Aufrufstelle und Quellposition, die der Compiler zur eindeutigen Identifizierung verwendet.
LoginScreen in der Komposition, wenn sich der Status ändert und eine Neuzusammensetzung erfolgt. Gleiche Farbe bedeutet, dass es nicht neu zusammengesetzt wurde.Auch wenn LoginInput nicht mehr zuerst, sondern als Zweites aufgerufen wird, bleibt die LoginInput-Instanz bei Neuzusammensetzungen erhalten. Da LoginInput keine Parameter hat, die sich bei der Neuzusammensetzung geändert haben, wird der Aufruf von LoginInput von Compose übersprungen.
Zusätzliche Informationen hinzufügen, um die intelligente Neuzusammensetzung zu verbessern
Wenn Sie eine zusammensetzbare Funktion mehrmals aufrufen, wird sie auch mehrmals zur Komposition hinzugefügt. Wenn eine zusammensetzbare Funktion mehrmals von derselben Aufrufstelle aufgerufen wird, hat Compose keine Informationen, um jeden Aufruf dieser zusammensetzbaren Funktion eindeutig zu identifizieren. Daher wird zusätzlich zur Aufrufstelle die Ausführungsreihenfolge verwendet, um die Instanzen voneinander zu unterscheiden. Dieses Verhalten ist manchmal alles, was erforderlich ist, kann aber in einigen Fällen zu unerwünschtem Verhalten führen.
@Composable fun MoviesScreen(movies: List<Movie>) { Column { for (movie in movies) { // MovieOverview composables are placed in Composition given its // index position in the for loop MovieOverview(movie) } } }
Im obigen Beispiel verwendet Compose zusätzlich zur Aufrufstelle die Ausführungsreihenfolge, um die Instanz in der Komposition eindeutig zu machen. Wenn am Ende der Liste ein neues movie hinzugefügt wird, kann Compose die bereits in der Composition vorhandenen Instanzen wiederverwenden, da sich ihre Position in der Liste nicht geändert hat und die movie-Eingabe für diese Instanzen daher dieselbe ist.
MoviesScreen in der Composition, wenn ein neues Element am Ende der Liste hinzugefügt wird. MovieOverview-Kompositionen in der Komposition können wiederverwendet werden. Dieselbe Farbe in MovieOverview bedeutet, dass das Compose-Element nicht neu zusammengesetzt wurde.Wenn sich die Liste movies jedoch ändert, indem entweder am Anfang oder in der Mitte der Liste Elemente hinzugefügt, entfernt oder neu angeordnet werden, führt dies zu einer Neuzusammensetzung in allen MovieOverview-Aufrufen, deren Eingabeparameter die Position in der Liste geändert hat. Das ist extrem wichtig, wenn MovieOverview beispielsweise ein Filmbild über einen Nebeneffekt abruft. Wenn die Neuzusammensetzung während des Effekts erfolgt, wird er abgebrochen und neu gestartet.
@Composable fun MovieOverview(movie: Movie) { Column { // Side effect explained later in the docs. If MovieOverview // recomposes, while fetching the image is in progress, // it is cancelled and restarted. val image = loadNetworkImage(movie.url) MovieHeader(image) /* ... */ } }
MoviesScreen in der Composition, wenn der Liste ein neues Element hinzugefügt wird. MovieOverview-Kompositionen können nicht wiederverwendet werden und alle Nebenwirkungen werden neu gestartet. Eine andere Farbe in MovieOverview bedeutet, dass das Compose-Element neu zusammengesetzt wurde.Idealerweise sollte die Identität der MovieOverview-Instanz mit der Identität des an sie übergebenen movie verknüpft sein. Wenn wir die Liste der Filme neu anordnen, sollten wir idealerweise auch die Instanzen im Kompositionsbaum neu anordnen, anstatt jedes MovieOverview-Composable mit einer anderen Filminstanz neu zu erstellen. Mit Compose können Sie der Laufzeit mitteilen, welche Werte Sie zur Identifizierung eines bestimmten Teils des Baums verwenden möchten: die komponierbare Funktion key.
Wenn Sie einen Codeblock mit einem Aufruf der zusammensetzbaren Funktion „key“ umschließen und einen oder mehrere Werte übergeben, werden diese Werte kombiniert, um die Instanz in der Komposition zu identifizieren. Der Wert für ein key muss nicht global eindeutig sein, sondern nur innerhalb der Aufrufe von Composables an der Aufrufstelle. In diesem Beispiel muss also jedes movie einen key haben, der innerhalb der movies eindeutig ist. Es ist in Ordnung, wenn es diesen key mit einer anderen zusammensetzbaren Funktion an einer anderen Stelle in der App teilt.
@Composable fun MoviesScreenWithKey(movies: List<Movie>) { Column { for (movie in movies) { key(movie.id) { // Unique ID for this movie MovieOverview(movie) } } } }
Mit dem oben genannten Code erkennt Compose einzelne Aufrufe von MovieOverview und kann sie wiederverwenden, auch wenn sich die Elemente in der Liste ändern.
MoviesScreen in der Composition, wenn der Liste ein neues Element hinzugefügt wird. Da die MovieOverview-Composables eindeutige Schlüssel haben, erkennt Compose, welche MovieOverview-Instanzen sich nicht geändert haben, und kann sie wiederverwenden. Ihre Nebenwirkungen werden weiterhin ausgeführt.Einige Composables bieten integrierte Unterstützung für das key-Composable. LazyColumn akzeptiert beispielsweise die Angabe eines benutzerdefinierten key in der items-DSL.
@Composable fun MoviesScreenLazy(movies: List<Movie>) { LazyColumn { items(movies, key = { movie -> movie.id }) { movie -> MovieOverview(movie) } } }
Überspringen, wenn sich die Eingaben nicht geändert haben
Während der Neuzusammensetzung kann die Ausführung einiger geeigneter zusammensetzbarer Funktionen vollständig übersprungen werden, wenn sich ihre Eingaben gegenüber der vorherigen Zusammensetzung nicht geändert haben.
Eine zusammensetzbare Funktion kann übersprungen werden, es sei denn:
- Die Funktion hat einen Rückgabetyp, der nicht
Unitist. - Die Funktion ist mit
@NonRestartableComposableoder@NonSkippableComposableannotiert. - Ein erforderlicher Parameter ist von einem nicht stabilen Typ
Es gibt einen Compiler-Modus, Strong Skipping, der die letzte Anforderung lockert.
Damit ein Typ als stabil gilt, muss er die folgenden Anforderungen erfüllen:
- Das Ergebnis von
equalsfür zwei Instanzen ist für dieselben zwei Instanzen für immer gleich. - Wenn sich ein öffentliches Attribut des Typs ändert, wird Composition benachrichtigt.
- Alle öffentlichen Property-Typen sind ebenfalls stabil.
Es gibt einige wichtige gängige Typen, die unter diesen Vertrag fallen und vom Compose-Compiler als stabil behandelt werden, obwohl sie nicht explizit mit der Annotation @Stable als stabil gekennzeichnet sind:
- Alle einfachen Werttypen:
Boolean,Int,Long,Float,Charusw. - Strings
- Alle Funktionstypen (Lambdas)
Alle diese Typen können den Vertrag von „stable“ einhalten, da sie unveränderlich sind. Da sich unveränderliche Typen nie ändern, müssen sie die Composition nie über die Änderung benachrichtigen. Es ist also viel einfacher, diesen Vertrag einzuhalten.
Ein wichtiger Typ, der stabil, aber veränderlich ist, ist der Compose-Typ MutableState. Wenn ein Wert in einem MutableState gespeichert ist, gilt das Zustandsobjekt insgesamt als stabil, da Compose über alle Änderungen an der .value-Eigenschaft von State benachrichtigt wird.
Wenn alle Typen, die als Parameter an eine zusammensetzbare Funktion übergeben werden, stabil sind, werden die Parameterwerte anhand der Position der zusammensetzbaren Funktion im UI-Baum auf Gleichheit verglichen. Die Neuzusammensetzung wird übersprungen, wenn sich seit dem vorherigen Aufruf keine Werte geändert haben.
Compose betrachtet einen Typ nur dann als stabil, wenn dies nachgewiesen werden kann. Eine Schnittstelle wird beispielsweise im Allgemeinen als nicht stabil behandelt. Typen mit änderbaren öffentlichen Attributen, deren Implementierung unveränderlich sein könnte, sind ebenfalls nicht stabil.
Wenn Compose nicht ableiten kann, dass ein Typ stabil ist, Sie aber möchten, dass Compose ihn als stabil behandelt, kennzeichnen Sie ihn mit der Annotation @Stable.
// Marking the type as stable to favor skipping and smart recompositions. @Stable interface UiState<T : Result<T>> { val value: T? val exception: Throwable? val hasError: Boolean get() = exception != null }
Im obigen Code-Snippet ist UiState eine Schnittstelle. Daher könnte Compose diesen Typ normalerweise als nicht stabil betrachten. Durch Hinzufügen der Annotation @Stable teilst du Compose mit, dass dieser Typ stabil ist, sodass Compose intelligente Neuzusammensetzungen bevorzugen kann. Das bedeutet auch, dass Compose alle Implementierungen als stabil behandelt, wenn die Schnittstelle als Parametertyp verwendet wird.
Empfehlungen für Sie
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Zustand und Jetpack Compose
- Nebeneffekte in Compose
- UI-Status in Compose speichern