Stabilitätsprobleme beheben

Wenn Sie eine instabile Klasse haben, die zu Leistungsproblemen führt, sollten Sie sie stabilisieren. In diesem Dokument werden mehrere Techniken beschrieben, die Sie dazu verwenden können.

Starkes Überspringen aktivieren

Versuchen Sie zuerst, den Modus für das Überspringen von Werbeunterbrechungen zu aktivieren. Im Modus „Starkes Überspringen“ können Kompositionen mit instabilen Parametern übersprungen werden. Das ist die einfachste Methode, um Leistungsprobleme zu beheben, die durch Stabilitätsprobleme verursacht werden.

Weitere Informationen finden Sie unter Starkes Überspringen.

Klasse unveränderlich machen

Sie können auch versuchen, eine instabile Klasse vollständig unveränderlich zu machen.

  • Unveränderlich: Gibt einen Typ an, bei dem sich der Wert der Eigenschaften nach dem Erstellen einer Instanz dieses Typs nicht mehr ändern kann. Alle Methoden sind referenziell transparent.
    • Achten Sie darauf, dass alle Attribute der Klasse sowohl val als var als auch unveränderliche Typen haben.
    • Einfache Typen wie String, Int und Float sind immer unveränderlich.
    • Wenn dies nicht möglich ist, müssen Sie für alle veränderbaren Properties den Compose-Status verwenden.
  • Stabil: Gibt einen Typ an, der änderbar ist. Die Compose-Laufzeit erkennt nicht, ob und wann eine der öffentlichen Eigenschaften oder das Methodenverhalten des Typs zu anderen Ergebnissen als bei einer vorherigen Aufrufung führen würde.

Unveränderliche Sammlungen

Ein häufiger Grund, warum Compose eine Klasse als instabil einstuft, sind Sammlungen. Wie auf der Seite Stabilitätsprobleme diagnostizieren angemerkt, kann mit dem Compiler „Composer“ nicht ganz sicher sein, ob Sammlungen wie List, Map und Set wirklich unveränderlich sind und daher als instabil markiert werden.

Um dieses Problem zu beheben, können Sie unveränderliche Sammlungen verwenden. Der Compose-Compiler unterstützt Kotlinx Immutable Collections. Diese Sammlungen sind garantiert unveränderlich und werden vom Compose-Compiler entsprechend behandelt. Diese Bibliothek befindet sich noch in der Alphaphase. Es kann also zu Änderungen an der API kommen.

Denken Sie noch einmal an diese instabile Klasse aus dem Leitfaden Stabilitätsprobleme diagnostizieren:

unstable class Snack {
  …
  unstable val tags: Set<String>
  …
}

Sie können tags mit einer unveränderlichen Sammlung stabil machen. Ändern Sie in der Klasse den Typ von tags in ImmutableSet<String>:

data class Snack{
    …
    val tags: ImmutableSet<String> = persistentSetOf()
    …
}

Danach sind alle Parameter der Klasse unveränderlich und der Compose-Compiler kennzeichnet die Klasse als stabil.

Mit Stable oder Immutable Anmerkungen hinzufügen

Sie können Stabilitätsprobleme beheben, indem Sie instabile Klassen mit @Stable oder @Immutable annotieren.

Wenn Sie eine Klasse annotieren, überschreiben Sie, was der Compiler sonst über Ihre Klasse inferieren würde. Er ähnelt dem Operator !! in Kotlin. Sie sollten genau darauf achten, wie Sie diese Anmerkungen verwenden. Wenn Sie das Verhalten des Compilers überschreiben, kann das zu unvorhergesehenen Fehlern führen, z. B. dazu, dass Ihr Composeable nicht wie erwartet neu zusammengesetzt wird.

Wenn es möglich ist, Ihre Klasse ohne Annotation stabil zu machen, sollten Sie auf diese Weise für Stabilität sorgen.

Das folgende Snippet zeigt ein minimales Beispiel für eine als unveränderlich annotierte Datenklasse:

@Immutable
data class Snack(
…
)

Unabhängig davon, ob Sie die Annotation @Immutable oder @Stable verwenden, markiert der Compose-Compiler die Snack-Klasse als stabil.

Mit Anmerkungen versehene Kurse in Sammlungen

Sehen wir uns eine zusammensetzbare Funktion an, die einen Parameter vom Typ List<Snack> enthält:

restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  …
  unstable snacks: List<Snack>
  …
)

Auch wenn Sie Snack mit @Immutable annotieren, kennzeichnet der Compose-Compiler den Parameter snacks in HighlightedSnacks weiterhin als instabil.

In Bezug auf Sammlungstypen haben Parameter das gleiche Problem wie Klassen. Der Compose-Compiler markiert einen Parameter des Typs List immer als instabil, auch wenn es sich um eine Sammlung stabiler Typen handelt.

Es ist nicht möglich, einen einzelnen Parameter als stabil zu kennzeichnen oder einen zusammensetzbaren Videocontent als immer überspringbar zu kennzeichnen. Es gibt mehrere Möglichkeiten, wie es weitergehen kann.

Es gibt mehrere Möglichkeiten, das Problem instabiler Sammlungen zu umgehen. In den folgenden Unterabschnitten werden diese verschiedenen Ansätze beschrieben.

Konfigurationsdatei

Wenn Sie den Stabilitätsvertrag in Ihrer Codebasis einhalten möchten, können Sie Kotlin-Sammlungen als stabil betrachten, indem Sie Ihrer Stabilitätskonfigurationsdatei das Zeichen kotlin.collections.* hinzufügen.

Unveränderliche Sammlung

Zur Sicherheit der Kompilierungszeit der Unveränderlichkeit können Sie anstelle von List eine unveränderliche Kotlinx-Sammlung verwenden.

@Composable
private fun HighlightedSnacks(
    …
    snacks: ImmutableList<Snack>,
    …
)

Wrapper

Wenn Sie eine unveränderliche Sammlung nicht verwenden können, können Sie Ihre eigene erstellen. Dazu müssen Sie List in eine annotierte stabile Klasse einschließen. Je nach Ihren Anforderungen ist ein generischer Wrapper wahrscheinlich die beste Wahl.

@Immutable
data class SnackCollection(
   val snacks: List<Snack>
)

Diesen können Sie dann als Typ des Parameters in Ihrer zusammensetzbaren Funktion verwenden.

@Composable
private fun HighlightedSnacks(
    index: Int,
    snacks: SnackCollection,
    onSnackClick: (Long) -> Unit,
    modifier: Modifier = Modifier
)

Lösung

Nach der Anwendung eines dieser Ansätze kennzeichnet der Compose-Compiler das HighlightedSnacks-Komposit jetzt sowohl als skippable als auch als restartable.

restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  stable index: Int
  stable snacks: ImmutableList<Snack>
  stable onSnackClick: Function1<Long, Unit>
  stable modifier: Modifier? = @static Companion
)

Bei der Neuzusammensetzung kann Compose HighlightedSnacks jetzt überspringen, wenn sich keine seiner Eingaben geändert haben.

Stabilitätskonfigurationsdatei

Ab Compose Compiler 1.5.5 kann zum Kompilieren eine Konfigurationsdatei mit Klassen bereitgestellt werden, die als stabil gelten. Dadurch können auch Klassen, die Sie nicht steuern, wie Standardbibliotheksklassen wie LocalDateTime als stabil betrachtet werden.

Die Konfigurationsdatei ist eine Nur-Text-Datei mit einer Klasse pro Zeile. Kommentare sowie einfache und doppelte Platzhalter werden unterstützt. Beispielkonfiguration:

// Consider LocalDateTime stable
java.time.LocalDateTime
// Consider my datalayer stable
com.datalayer.*
// Consider my datalayer and all submodules stable
com.datalayer.**
// Consider my generic type stable based off it's first type parameter only
com.example.GenericClass<*,_>

Zum Aktivieren dieser Funktion übergeben Sie den Pfad der Konfigurationsdatei an den Optionsblock composeCompiler der Konfiguration des Composer-Gradle-Plug-ins.

composeCompiler {
  stabilityConfigurationFile = rootProject.layout.projectDirectory.file("stability_config.conf")
}

Da der Compose-Compiler auf jedem Modul in Ihrem Projekt separat ausgeführt wird, können Sie bei Bedarf unterschiedliche Konfigurationen für verschiedene Module angeben. Alternativ können Sie eine Konfiguration auf der Stammebene Ihres Projekts haben und diesen Pfad an jedes Modul übergeben.

Mehrere Module

Ein weiteres häufiges Problem betrifft die Architektur mit mehreren Modulen. Der Compose-Compiler kann nur ableiten, ob eine Klasse stabil ist, wenn alle nicht-primitiven Typen, auf die sie verweist, entweder explizit als stabil markiert sind oder sich in einem Modul befinden, das auch mit dem Compose-Compiler erstellt wurde.

Wenn sich Ihre Datenebene in einem separaten Modul von Ihrer UI-Ebene befindet, was der empfohlene Ansatz ist, kann dies zu Problemen führen.

Lösung

Sie haben folgende Möglichkeiten, dieses Problem zu beheben:

  1. Fügen Sie die Klassen der Compiler-Konfigurationsdatei hinzu.
  2. Aktivieren Sie den Compose-Compiler in Ihren Datenebenen-Modulen oder taggen Sie Ihre Klassen gegebenenfalls mit @Stable oder @Immutable.
    • Dazu müssen Sie Ihrer Datenebene eine Compose-Abhängigkeit hinzufügen. Es ist jedoch nur die Abhängigkeit für die Compose-Laufzeit und nicht für Compose-UI.
  3. Umschließen Sie die Klassen der Datenebene in Ihrem UI-Modul in UI-spezifischen Wrapper-Klassen.

Das gleiche Problem tritt auch auf, wenn externe Bibliotheken ohne den Composer-Compiler verwendet werden.

Nicht jede zusammensetzbare Funktion sollte überspringbar sein.

Wenn Sie Stabilitätsprobleme beheben, sollten Sie nicht versuchen, jede zusammensetzbare Funktion überspringbar zu machen. Der Versuch, dies zu tun, kann zu einer vorzeitigen Optimierung führen, die mehr Probleme mit sich bringt als behoben wird.

Es gibt viele Situationen, in denen überspringbare Anzeigen keine wirklichen Vorteile haben und dazu führen können, dass der Code schwer zu verwalten ist. Beispiel:

  • Eine zusammensetzbare Funktion, die nicht oft oder überhaupt neu zusammengesetzt wird.
  • Ein Composeable, das nur überspringbare Composeables aufruft.
  • Ein Komposit mit einer großen Anzahl von Parametern mit teuren „equals“-Implementierungen. In diesem Fall können die Kosten für die Prüfung, ob sich ein Parameter geändert hat, die Kosten für eine kostengünstige Neuzusammensetzung übersteigen.

Wenn eine zusammensetzbare Funktion überspringbar ist, entsteht ein geringer Mehraufwand, der sich möglicherweise nicht lohnt. Sie können Ihre zusammensetzbare Funktion sogar als nicht neustartbar kennzeichnen, wenn Sie feststellen, dass ein Neustart mehr Aufwand verursacht, als es wert ist.