Lebenszyklus von zusammensetzbaren Funktionen

Auf dieser Seite erfahren Sie mehr über den Lebenszyklus eines Composeables und wie in Compose entschieden wird, ob ein Composeable neu zusammengesetzt werden muss.

Lebenszyklusübersicht

Wie in der Dokumentation zur Verwaltung des Status erwähnt, ist eine Zusammensetzung beschreibt die Benutzeroberfläche Ihrer App und wird durch das Ausführen von zusammensetzbaren Funktionen erstellt. Eine Komposition ist eine Baumstruktur der zusammensetzbaren Funktionen, die Ihre Benutzeroberfläche beschreiben.

Wenn Jetpack Compose Ihre zusammensetzbaren Funktionen zum ersten Mal ausführt, während für den Anfang Zusammensetzung, erfasst sie die zusammensetzbaren Funktionen, die Sie zur Beschreibung in einer Komposition. Wenn sich der Status Ihrer App ändert, plant Jetpack Compose eine Neuzusammenstellung. Bei der Neuzusammensetzung führt Jetpack Compose die Compose-Objekte, die sich möglicherweise aufgrund von Statusänderungen geändert haben, noch einmal aus und aktualisiert dann die Zusammensetzung entsprechend.

Eine Komposition kann nur durch eine ursprüngliche Komposition produziert und von Neuzusammensetzung. Eine Komposition kann nur durch Neuzusammenstellung geändert werden.

Diagramm, das den Lebenszyklus eines Composeable zeigt

Abbildung 1. Lebenszyklus eines Composeables in der Komposition. Es wird in die Komposition eingefügt, null oder mehrmals neu komponiert und verlässt die Komposition.

Die Neuzusammensetzung wird in der Regel durch eine Änderung an einer State<T>-Objekt. Verfassen verfolgt sie und führt alle zusammensetzbaren Funktionen in der Komposition aus, die Folgendes lautet: bestimmte State<T> sowie alle zusammensetzbaren Funktionen, die sie aufrufen und die nicht übersprungen.

Wenn eine zusammensetzbare Funktion mehrmals aufgerufen wird, werden mehrere Instanzen im Komposition. Jeder Aufruf hat in der Zusammensetzung seinen eigenen Lebenszyklus.

@Composable
fun MyComposable() {
    Column {
        Text("Hello")
        Text("World")
    }
}

Diagramm, das die hierarchische Anordnung der Elemente im vorherigen Code-Snippet zeigt

Abbildung 2. Darstellung von MyComposable in der Komposition. Wenn ein Composeable mehrmals aufgerufen wird, werden mehrere Instanzen in die Komposition eingefügt. Ein Element mit einer anderen Farbe weist auf eine separate Instanz hin.

Anatomie einer Zusammensetzung in einer Komposition

Die Instanz einer zusammensetzbaren Funktion in der Komposition wird durch die entsprechende Aufrufwebsite identifiziert. Der Compose-Compiler betrachtet jede Aufruf-Website als eigenständige Website. Wenn Sie Composeables von mehreren Aufrufstellen aus aufrufen, werden mehrere Instanzen des Composeables in der Zusammensetzung erstellt.

Wenn bei einer Neuzusammensetzung ein Compose-Element andere Compose-Elemente aufruft als bei der vorherigen Zusammensetzung, ermittelt Compose, welche Compose-Elemente aufgerufen wurden oder nicht. Bei den Compose-Elementen, die in beiden Zusammensetzungen aufgerufen wurden, vermeidet Compose eine Neuzusammensetzung, wenn sich ihre Eingaben nicht geändert haben.

Die Identität muss beibehalten werden, damit Nebeneffekte mit ihren komponierbaren Elementen verknüpft werden können, damit sie erfolgreich abgeschlossen werden können, anstatt bei jeder Neuzusammensetzung neu gestartet zu werden.

Betrachten Sie das folgende 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 LoginError-Komposition bedingt und die LoginInput-Komposition immer auf. Jedes -Aufruf hat eine eindeutige Aufrufwebsite und Quellposition, die vom Compiler verwendet werden, um eindeutig identifiziert werden.

Diagramm, das zeigt, wie der vorherige Code neu zusammengesetzt wird, wenn das Flag showError in „true“ geändert wird. Die zusammensetzbare Funktion „LoginError“ wird hinzugefügt, aber die anderen zusammensetzbaren Funktionen werden nicht neu zusammengesetzt.

Abbildung 3: Darstellung von LoginScreen in der Komposition, wenn sich der Status ändert und eine Neuzusammensetzung erfolgt. Die gleiche Farbe bedeutet, dass es nicht neu zusammengesetzt wurde.

LoginInput wurde zwar statt der ersten zu einer zweiten, aber Die Instanz LoginInput wird bei allen Neuzusammensetzungen beibehalten. Außerdem weil es bei LoginInput keine Parameter gibt, die sich im Laufe der Zeit Neuzusammensetzung ist, wird der Aufruf von LoginInput mit der Funktion "Compose" übersprungen.

Zusätzliche Informationen für eine intelligente Neuzusammensetzung hinzufügen

Wenn Sie ein Composeable mehrmals aufrufen, wird es der Komposition ebenfalls mehrmals hinzugefügt. Wenn ein Composeable mehrmals von derselben Aufrufstelle aufgerufen wird, hat Compose keine Informationen, um jeden Aufruf dieses Composeables eindeutig zu identifizieren. Daher wird zusätzlich zur Aufrufstelle die Ausführungsreihenfolge verwendet, um die Instanzen voneinander zu unterscheiden. Manchmal reicht dieses Verhalten aus, kann es unerwünschtes Verhalten verursachen.

@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 Beispiel oben verwendet Compose zusätzlich zur Aufruf-Website die Ausführungsreihenfolge, um die Instanz in der Komposition eindeutig zu halten. Wenn eine neue movie hinzugefügt wird unten der Liste können Sie mit der Funktion "Compose" die Instanzen wiederverwenden, die bereits in der Da sich die Zusammensetzung in der Liste nicht geändert hat, Die movie-Eingabe ist für diese Instanzen gleich.

Diagramm, das zeigt, wie der vorherige Code neu zusammengesetzt wird, wenn am Ende der Liste ein neues Element hinzugefügt wird. Die Position der anderen Elemente in der Liste hat sich nicht geändert und sie werden nicht neu zusammengesetzt.

Abbildung 4: Darstellung von MoviesScreen in der Komposition, wenn der Liste unten ein neues Element hinzugefügt wird. MovieOverview zusammensetzbare Funktionen in der Zusammensetzung kann wiederverwendet werden. Die gleiche Farbe in MovieOverview bedeutet, dass die zusammensetzbare Funktion nicht neu zusammengesetzt wurde.

Wenn sich die movies-Liste jedoch ändert, indem Elemente oben oder in der Mitte hinzugefügt, entfernt oder neu angeordnet werden, wird bei allen MovieOverview-Aufrufen, deren Eingabeparameter sich in der Liste verschoben hat, eine Neuzusammensetzung durchgeführt. Das ist zum Beispiel sehr wichtig, wenn MovieOverview Filmbild mit einem Nebeneffekt hinzu. Wenn die Neuzusammensetzung während der Ausführung 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)

        /* ... */
    }
}

Diagramm, das zeigt, wie der vorherige Code neu zusammengesetzt wird, wenn der Liste oben ein neues Element hinzugefügt wird Alle anderen Elemente in der Liste ändern ihre Position und müssen neu zusammengesetzt werden.

Abbildung 5: Darstellung von MoviesScreen in der Komposition, wenn eine neue wird der Liste hinzugefügt. MovieOverview-Kompositionen können nicht wiederverwendet werden und alle Nebenwirkungen werden neu gestartet. Eine andere Farbe in MovieOverview bedeutet, dass das Composed-Element neu zusammengesetzt wurde.

Idealerweise sollten wir uns die Identität der MovieOverview-Instanz mit der Identität der movie verknüpft ist, die an sie übergeben wird. Wenn wir die Spalten Idealerweise würden wir die Instanzen im Feld Zusammensetzungsbaum, anstatt jede zusammensetzbare Funktion MovieOverview mit einem eine andere Filminstanz. Mit Compose können Sie der Laufzeit mitteilen, welche Werte Sie zum Identifizieren eines bestimmten Teils des Baums verwenden möchten: das key-komposit.

Wenn Sie einen Codeblock in einen Aufruf des Schlüssels einschließen, der mit einem oder mehreren übergebenen Werten zusammengesetzt werden kann, werden diese Werte kombiniert, um diese Instanz in der Komposition zu identifizieren. Der Wert für key muss nicht global eindeutig sein darf, darf sie nur unter den Aufrufen von zusammensetzbaren Funktionen auf der Call-Site verwenden. In diesem Beispiel muss also jede movie eine key haben, die unter den movies eindeutig ist. Es ist in Ordnung, wenn diese key mit einer anderen zusammensetzbaren Komponente an anderer Stelle in der App geteilt wird.

@Composable
fun MoviesScreenWithKey(movies: List<Movie>) {
    Column {
        for (movie in movies) {
            key(movie.id) { // Unique ID for this movie
                MovieOverview(movie)
            }
        }
    }
}

So werden auch dann, wenn sich die Elemente in der Liste ändern, einzelne Aufrufe von MovieOverview in Compose erkannt und können wiederverwendet werden.

Diagramm, das zeigt, wie der vorherige Code neu zusammengesetzt wird, wenn ein neues Element an den Anfang der Liste gesetzt wird. Da die Listenelemente anhand von Schlüsseln identifiziert werden, weiß Compose, dass sie nicht neu zusammengesetzt werden müssen, auch wenn sich ihre Positionen geändert haben.

Abbildung 6 Darstellung von MoviesScreen in der Komposition, wenn der Liste ein neues Element hinzugefügt wird. Da die zusammensetzbaren Funktionen von MovieOverview eindeutige erkennt Compose, welche MovieOverview Instanzen unverändert sind, und sie wiederverwenden; dass ihre Nebenwirkungen weiterhin ausgeführt werden.

Einige Composeables bieten integrierte Unterstützung für das key-Composeable. Beispiel: LazyColumn akzeptiert die Angabe einer benutzerdefinierten key in 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

Bei der Neuzusammensetzung können einige zusammensetzbare Funktionen ihre die Ausführung vollständig übersprungen, wenn sich ihre Eingaben im Vergleich zur vorherigen nicht geändert haben. Zusammensetzung.

Eine zusammensetzbare Funktion kann übersprungen werden, außer

  • Die Funktion hat einen Rückgabetyp, der nicht Unit ist
  • Die Funktion ist mit @NonRestartableComposable oder @NonSkippableComposable annotiert.
  • Ein erforderlicher Parameter hat einen instabilen Typ

Es gibt einen experimentellen Compilermodus, Strong Skipping, bei dem die letzte Anforderung gelockert wird.

Damit ein Typ als stabil gilt, muss er folgende Anforderungen erfüllen: Vertrag:

  • Das Ergebnis von equals für zwei Instanzen ist immer das gleiche für die dieselben zwei Instanzen.
  • Wenn sich ein öffentliches Eigentum dieses Typs ändert, wird die Komposition benachrichtigt.
  • Alle Arten öffentlicher Immobilien sind ebenfalls unverändert.

Es gibt einige wichtige gängige Arten, die in diesen Vertrag fallen, Der Compose-Compiler wird als stabil behandelt, auch wenn er nicht explizit mit der Anmerkung @Stable als stabil markiert:

  • Alle primitiven Werttypen: Boolean, Int, Long, Float, Char usw.
  • Strings
  • Alle Funktionstypen (Lambdas)

Für alle diese Typen gilt ein stabiler Vertrag, unveränderlich. Da sich unveränderliche Typen nie ändern, müssen sie die Zusammensetzung der Änderung nie benachrichtigen. Daher ist es viel einfacher, diesen Vertrag einzuhalten.

Ein bemerkenswerter Typ, der stabil ist, aber änderbar ist, ist MutableState Typ. Wenn ein Wert in einer MutableState gespeichert wird, gilt das Statusobjekt insgesamt als stabil, da Compose über alle Änderungen an der .value-Eigenschaft von State informiert wird.

Wenn alle Typen, die als Parameter an eine zusammensetzbare Funktion übergeben werden, stabil sind, wird der Parameter werden die Werte anhand der zusammensetzbaren Position in der Benutzeroberfläche auf Gleichheit geprüft. Baum. Die Neuzusammensetzung wird übersprungen, wenn sich alle Werte seit dem vorherigen Aufruf nicht geändert haben.

Compose betrachtet einen Typ nur dann als stabil, wenn er dies nachweisen kann. Beispielsweise wird eine Schnittstelle im Allgemeinen als nicht stabil behandelt. Auch Typen mit veränderlichen öffentlichen Eigenschaften, deren Implementierung unveränderlich sein könnte, sind nicht stabil.

Wenn Compose nicht in der Lage ist, zu erkennen, dass ein Typ stabil ist, Sie ihn aber zwingen möchten, ihn als stabil zu behandeln, kennzeichnen Sie ihn mit der Anmerkung @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
}

Da es sich bei UiState um eine Benutzeroberfläche handelt, wird dieser Typ üblicherweise als nicht stabil erachtet. Durch Hinzufügen der @Stable kennzeichnen Sie Compose, dass dieser Typ stabil ist, sodass "Compose" bevorzugt kluge Neuzusammensetzungen. Das bedeutet auch, dass die Funktion "Compose" alle Implementierungen als stabil, wenn das Interface als Parametertyp verwendet wird.