Jetpack Compose ist ein modernes deklaratives UI-Toolkit für Android. Compose vereinfacht das Schreiben und Verwalten der Benutzeroberfläche Ihrer App, da es eine deklarative API bietet, mit der Sie die Benutzeroberfläche Ihrer App rendern können, ohne Frontend-Ansichten imperativ zu ändern. Diese Terminologie muss erläutert werden, aber die Auswirkungen sind wichtig für das Design Ihrer App.
Das deklarative Programmierparadigma
Bisher wurde eine Android-View-Hierarchie als Baum von UI-Widgets dargestellt. Wenn sich der Status der App aufgrund von Nutzerinteraktionen ändert, muss die UI-Hierarchie aktualisiert werden, um die aktuellen Daten anzuzeigen.
Die Benutzeroberfläche wird am häufigsten aktualisiert, indem der Baum mit Funktionen wie findViewById()
durchlaufen und Knoten durch Aufrufen von Methoden wie button.setText(String)
, container.addChild(View)
oder img.setImageBitmap(Bitmap)
geändert werden. Diese Methoden ändern den internen Status des Widgets.
Wenn Sie Ansichten manuell bearbeiten, steigt die Wahrscheinlichkeit von Fehlern. Wenn ein Datenelement an mehreren Stellen gerendert wird, vergessen Sie möglicherweise, eine der Ansichten zu aktualisieren, in der es angezeigt wird. Dies kann auch zu illegalen Zuständen führen, wenn zwei Updates auf unerwartete Weise in Konflikt geraten. Beispiel: Bei einer Aktualisierung wird versucht, einen Wert für einen Knoten festzulegen, der gerade aus der Benutzeroberfläche entfernt wurde. Im Allgemeinen steigt die Komplexität der Softwarewartung mit der Anzahl der Ansichten, die aktualisiert werden müssen.
In den letzten Jahren hat sich die gesamte Branche auf ein deklaratives UI-Modell umgestellt. Dieses Modell vereinfacht die Entwicklung und Aktualisierung von Benutzeroberflächen. Bei dieser Technik wird der gesamte Bildschirm konzeptionell neu generiert und dann werden nur die erforderlichen Änderungen angewendet. Mit diesem Ansatz wird die Komplexität der manuellen Aktualisierung einer zustandsbehafteten Ansichtshierarchie vermieden. Compose ist ein deklaratives UI-Framework.
Eine Herausforderung bei der Neuerstellung des gesamten Bildschirms besteht darin, dass dies in Bezug auf Zeit, Rechenleistung und Akkunutzung potenziell aufwendig ist. Um diese Kosten zu minimieren, wählt Compose intelligent aus, welche Teile der Benutzeroberfläche zu einem bestimmten Zeitpunkt neu gezeichnet werden müssen. Dies hat einige Auswirkungen auf das Design Ihrer UI-Komponenten, wie im Abschnitt Recomposition beschrieben.
Beispiel für eine komponierbare Funktion
Mit Compose können Sie Ihre Benutzeroberfläche erstellen, indem Sie eine Reihe von zusammensetzbaren Funktionen definieren, die Daten entgegennehmen und UI-Elemente ausgeben. Ein Beispiel ist ein Greeting
-Widget, das ein String
-Objekt entgegennimmt und ein Text
-Widget ausgibt, das eine Begrüßungsnachricht anzeigt.

Einige wichtige Punkte zu dieser Funktion:
- Anmerkung:Die Funktion ist mit der Anmerkung
@Composable
versehen. Alle zusammensetzbaren Funktionen müssen diese Annotation haben. Diese Annotation informiert den Compose-Compiler darüber, dass diese Funktion Daten in die Benutzeroberfläche umwandeln soll. - Dateneingabe:Die Funktion verwendet Daten als Eingabe. Composable-Funktionen können Parameter akzeptieren, mit denen die App-Logik die Benutzeroberfläche beschreibt. In diesem Fall akzeptiert das Widget eine
String
, damit der Nutzer mit seinem Namen begrüßt werden kann. - UI-Anzeige:Die Funktion zeigt Text in der Benutzeroberfläche an. Dazu wird die kombinierbare Funktion
Text()
aufgerufen, die das Text-UI-Element erstellt. Composable-Funktionen geben die UI-Hierarchie aus, indem sie andere Composable-Funktionen aufrufen. - Kein Rückgabewert:Die Funktion gibt nichts zurück. Compose-Funktionen, die UI ausgeben, müssen nichts zurückgeben, da sie den Zielbildschirmstatus beschreiben, anstatt UI-Widgets zu erstellen.
Eigenschaften:Diese Funktion ist schnell, idempotent und hat keine Nebeneffekte.
- Die Funktion verhält sich gleich, wenn sie mehrmals mit demselben Argument aufgerufen wird. Sie verwendet keine anderen Werte wie globale Variablen oder Aufrufe von
random()
. - Die Funktion beschreibt die Benutzeroberfläche ohne Nebeneffekte wie das Ändern von Eigenschaften oder globalen Variablen.
Im Allgemeinen müssen alle zusammensetzbaren Funktionen mit diesen Eigenschaften geschrieben werden. Die Gründe dafür werden im Abschnitt Neuaufbau erläutert.
- Die Funktion verhält sich gleich, wenn sie mehrmals mit demselben Argument aufgerufen wird. Sie verwendet keine anderen Werte wie globale Variablen oder Aufrufe von
Der Paradigmenwechsel hin zu deklarativen Ansätzen
Bei vielen imperativen objektorientierten UI-Toolkits initialisieren Sie die Benutzeroberfläche, indem Sie einen Baum von Widgets instanziieren. Dazu wird häufig eine XML-Layoutdatei aufgebläht. Jedes Widget verwaltet seinen eigenen internen Status und stellt Getter- und Setter-Methoden bereit, mit denen die App-Logik mit dem Widget interagieren kann.
Im deklarativen Ansatz von Compose sind Widgets relativ zustandslos und bieten keine Setter- oder Getter-Funktionen. Tatsächlich werden Widgets nicht als Objekte verfügbar gemacht.
Sie aktualisieren die Benutzeroberfläche, indem Sie dieselbe zusammensetzbare Funktion mit anderen Argumenten aufrufen. Dadurch wird die Bereitstellung von Status für Architekturmuster wie ViewModel
vereinfacht, wie im Leitfaden zur App-Architektur beschrieben. Ihre Composables sind dann dafür verantwortlich, den aktuellen Anwendungsstatus in eine Benutzeroberfläche umzuwandeln, wenn die beobachtbaren Daten aktualisiert werden.

Wenn der Nutzer mit der Benutzeroberfläche interagiert, werden Ereignisse wie onClick
ausgelöst.
Diese Ereignisse sollten die App-Logik benachrichtigen, die dann den Status der App ändern kann.
Wenn sich der Status ändert, werden die zusammensetzbaren Funktionen mit den neuen Daten noch einmal aufgerufen. Dadurch werden die UI-Elemente neu gezeichnet. Dieser Vorgang wird als Neuzusammensetzung bezeichnet.

Dynamischer Content
Da zusammensetzbare Funktionen in Kotlin statt in XML geschrieben werden, können sie so dynamisch sein wie jeder andere Kotlin-Code. Angenommen, Sie möchten eine Benutzeroberfläche erstellen, die eine Liste von Nutzern begrüßt:
@Composable fun Greeting(names: List<String>) { for (name in names) { Text("Hello $name") } }
Diese Funktion nimmt eine Liste von Namen entgegen und generiert für jeden Nutzer eine Begrüßung.
Composable-Funktionen können sehr komplex sein. Mit if
-Anweisungen können Sie festlegen, ob ein bestimmtes UI-Element angezeigt werden soll. Sie können Schleifen verwenden. Sie können Hilfsfunktionen aufrufen. Sie haben die volle Flexibilität der zugrunde liegenden Sprache.
Diese Leistungsfähigkeit und Flexibilität sind einer der wichtigsten Vorteile von Jetpack Compose.
Neuzusammensetzung
In einem imperativen UI-Modell rufen Sie zum Ändern eines Widgets einen Setter für das Widget auf, um seinen internen Status zu ändern. In Compose rufen Sie die zusammensetzbare Funktion noch einmal mit neuen Daten auf. Dadurch wird die Funktion neu zusammengesetzt. Die von der Funktion ausgegebenen Widgets werden bei Bedarf mit neuen Daten neu gezeichnet. Das Compose-Framework kann nur die Komponenten neu zusammensetzen, die sich geändert haben.
Sehen Sie sich beispielsweise diese zusammensetzbare Funktion an, mit der eine Schaltfläche angezeigt wird:
@Composable fun ClickCounter(clicks: Int, onClick: () -> Unit) { Button(onClick = onClick) { Text("I've been clicked $clicks times") } }
Jedes Mal, wenn auf die Schaltfläche geklickt wird, aktualisiert der Aufrufer den Wert von clicks
.
Compose ruft die Lambda-Funktion mit der Funktion Text
noch einmal auf, um den neuen Wert anzuzeigen. Dieser Vorgang wird als Neuzusammensetzung bezeichnet. Andere Funktionen, die nicht vom Wert abhängen, werden nicht neu zusammengesetzt.
Wie bereits erwähnt, kann das Neuzusammenstellen des gesamten UI-Baums rechenintensiv sein und somit Rechenleistung und Akkulaufzeit beanspruchen. Compose löst dieses Problem mit der intelligenten Neugestaltung.
Bei der Neukomposition werden Ihre zusammensetzbaren Funktionen noch einmal aufgerufen, wenn sich die Eingaben ändern. Wenn Compose aufgrund neuer Eingaben neu rendert, werden nur die Funktionen oder Lambdas aufgerufen, die sich möglicherweise geändert haben. Der Rest wird übersprungen. Wenn Funktionen oder Lambdas mit unveränderten Parametern übersprungen werden, wird Compose effizient neu zusammengesetzt.
Verlassen Sie sich niemals auf Nebeneffekte, die durch das Ausführen zusammensetzbarer Funktionen entstehen, da die Neuzusammensetzung einer Funktion übersprungen werden kann. Andernfalls kann es zu unerwartetem Verhalten in Ihrer App kommen. Eine Nebenwirkung ist jede Änderung, die für den Rest Ihrer App sichtbar ist. Die folgenden Aktionen sind beispielsweise gefährliche Nebenwirkungen:
- In eine Property eines freigegebenen Objekts schreiben
- Aktualisieren eines beobachtbaren Elements in
ViewModel
- Gemeinsame Einstellungen aktualisieren
Composable-Funktionen können so oft wie jedes Frame neu ausgeführt werden, z. B. wenn eine Animation gerendert wird. Composable-Funktionen sollten schnell sein, um Ruckeln bei Animationen zu vermeiden. Wenn Sie rechenintensive Vorgänge ausführen müssen, z. B. das Lesen aus freigegebenen Einstellungen, sollten Sie dies in einer Hintergrund-Coroutine tun und das Ergebnis als Parameter an die zusammensetzbare Funktion übergeben.
Mit diesem Code wird beispielsweise eine Composable-Funktion erstellt, um einen Wert in SharedPreferences
zu aktualisieren. Die Composable sollte nicht selbst aus freigegebenen Einstellungen lesen oder in sie schreiben. Stattdessen werden mit diesem Code die Lese- und Schreibvorgänge in eine ViewModel
in einer Hintergrund-Coroutine verschoben. Die App-Logik übergibt den aktuellen Wert mit einem Callback, um eine Aktualisierung auszulösen.
@Composable fun SharedPrefsToggle( text: String, value: Boolean, onValueChanged: (Boolean) -> Unit ) { Row { Text(text) Checkbox(checked = value, onCheckedChange = onValueChanged) } }
In diesem Dokument werden einige Punkte behandelt, die Sie bei der Verwendung von Compose beachten sollten:
- Bei der Neukomposition werden so viele zusammensetzbare Funktionen und Lambdas wie möglich übersprungen.
- Die Neukomposition ist optimistisch und kann abgebrochen werden.
- Eine zusammensetzbare Funktion wird möglicherweise sehr häufig ausgeführt, manchmal sogar für jeden Frame einer Animation.
- Composable-Funktionen können parallel ausgeführt werden.
- Composable-Funktionen können in beliebiger Reihenfolge ausgeführt werden.
In den folgenden Abschnitten wird beschrieben, wie Sie zusammensetzbare Funktionen erstellen, die die Neukomposition unterstützen. In jedem Fall ist es am besten, wenn Ihre zusammensetzbaren Funktionen schnell, idempotent und frei von Nebeneffekten sind.
Bei der Neukomposition werden so viele Elemente wie möglich übersprungen
Wenn Teile Ihrer Benutzeroberfläche ungültig sind, versucht Compose, nur die Teile neu zu rendern, die aktualisiert werden müssen. Das bedeutet, dass die Ausführung eines einzelnen zusammensetzbaren Button
übersprungen werden kann, ohne dass die zusammensetzbaren Elemente, die sich im UI-Baum darüber oder darunter befinden, ausgeführt werden.
Jede zusammensetzbare Funktion und jedes Lambda kann neu zusammengesetzt werden. Im folgenden Beispiel wird gezeigt, wie bei der Neukomposition einige Elemente beim Rendern einer Liste übersprungen werden können:
/** * Display a list of names the user can click with a header */ @Composable fun NamePicker( header: String, names: List<String>, onNameClicked: (String) -> Unit ) { Column { // this will recompose when [header] changes, but not when [names] changes Text(header, style = MaterialTheme.typography.bodyLarge) HorizontalDivider() // LazyColumn is the Compose version of a RecyclerView. // The lambda passed to items() is similar to a RecyclerView.ViewHolder. LazyColumn { items(names) { name -> // When an item's [name] updates, the adapter for that item // will recompose. This will not recompose when [header] changes NamePickerItem(name, onNameClicked) } } } } /** * Display a single name the user can click. */ @Composable private fun NamePickerItem(name: String, onClicked: (String) -> Unit) { Text(name, Modifier.clickable(onClick = { onClicked(name) })) }
Jeder dieser Bereiche kann das Einzige sein, was während einer Neuzusammenstellung ausgeführt wird.
Compose kann direkt zur Column
-Lambda-Funktion springen, ohne die übergeordneten Funktionen auszuführen, wenn sich header
ändert. Bei der Ausführung von Column
kann es vorkommen, dass Compose die Elemente von LazyColumn
überspringt, wenn sich names
nicht geändert hat.
Auch hier gilt, dass alle zusammensetzbaren Funktionen oder Lambdas keine Nebeneffekte haben sollten. Wenn Sie einen Nebeneffekt ausführen müssen, lösen Sie ihn über einen Callback aus.
Die Empfehlungen zur Körperzusammensetzung sind optimistisch.
Die Neuzusammensetzung beginnt immer dann, wenn Compose davon ausgeht, dass sich die Parameter einer Composable geändert haben könnten. Die Neukomposition ist optimistisch. Das bedeutet, dass Compose davon ausgeht, dass die Neukomposition abgeschlossen ist, bevor sich die Parameter wieder ändern. Wenn sich ein Parameter ändert, bevor die Neuzusammensetzung abgeschlossen ist, kann es sein, dass Compose die Neuzusammensetzung abbricht und mit dem neuen Parameter neu startet.
Wenn die Neuzusammenstellung abgebrochen wird, verwirft Compose den UI-Baum aus der Neuzusammenstellung. Wenn Sie Nebenwirkungen haben, die davon abhängen, dass die Benutzeroberfläche angezeigt wird, wird die Nebenwirkung auch dann angewendet, wenn die Komposition abgebrochen wird. Dies kann zu einem inkonsistenten App-Status führen.
Prüfen Sie, ob alle zusammensetzbaren Funktionen und Lambdas idempotent und frei von Nebeneffekten sind, um eine optimistische Neukomposition zu ermöglichen.
Komponierbare Funktionen werden möglicherweise sehr häufig ausgeführt
In einigen Fällen wird eine zusammensetzbare Funktion für jeden Frame einer UI-Animation ausgeführt. Wenn die Funktion rechenintensive Vorgänge ausführt, z. B. das Lesen aus dem Gerätespeicher, kann dies zu Rucklern in der Benutzeroberfläche führen.
Wenn Ihr Widget beispielsweise versucht, Geräteeinstellungen zu lesen, könnte es diese Einstellungen potenziell Hunderte Male pro Sekunde lesen, was sich katastrophal auf die Leistung Ihrer App auswirken würde.
Wenn eine zusammensetzbare Funktion Daten benötigt, definieren Sie Parameter für diese Daten. Sie können dann rechenintensive Vorgänge in einen anderen Thread außerhalb der Komposition verschieben und den resultierenden Wert mithilfe von mutableStateOf
oder LiveData
als Parameter an die zusammensetzbare Funktion übergeben.
Komponierbare Funktionen können parallel ausgeführt werden
Compose kann die Neuzusammenstellung optimieren, indem Composable-Funktionen parallel ausgeführt werden. So kann Compose mehrere Kerne nutzen und zusammensetzbare Funktionen, die nicht auf dem Bildschirm angezeigt werden, mit niedrigerer Priorität ausführen.
Bei dieser Optimierung wird eine zusammensetzbare Funktion möglicherweise in einem Pool von Hintergrund-Threads ausgeführt.
Wenn eine zusammensetzbare Funktion eine Funktion für ein ViewModel
aufruft, kann Compose diese Funktion möglicherweise gleichzeitig von mehreren Threads aus aufrufen.
Damit Ihre Anwendung korrekt funktioniert, sollten alle zusammensetzbaren Funktionen keine Nebeneffekte haben. Lösen Sie stattdessen Nebenwirkungen über Callbacks wie onClick
aus, die immer im UI-Thread ausgeführt werden.
Wenn eine zusammensetzbare Funktion aufgerufen wird, kann der Aufruf in einem anderen Thread als dem des Aufrufers erfolgen. Das bedeutet, dass Code, der Variablen in einem zusammensetzbaren Lambda ändert, vermieden werden sollte. Das liegt daran, dass solcher Code nicht threadsicher ist und einen unzulässigen Nebeneffekt des zusammensetzbaren Lambdas darstellt.
Hier ist ein Beispiel für eine Composable, die eine Liste und ihre Anzahl anzeigt:
@Composable fun ListComposable(myList: List<String>) { Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Text("Item: $item") } } Text("Count: ${myList.size}") } }
Dieser Code ist frei von Nebeneffekten und transformiert die Eingabeliste in die Benutzeroberfläche. Dieser Code eignet sich hervorragend zum Anzeigen einer kleinen Liste. Wenn die Funktion jedoch in eine lokale Variable schreibt, ist dieser Code nicht threadsicher oder korrekt:
@Composable fun ListWithBug(myList: List<String>) { var items = 0 Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Card { Text("Item: $item") items++ // Avoid! Side-effect of the column recomposing. } } } Text("Count: $items") } }
In diesem Beispiel wird items
bei jeder Neuzusammenstellung geändert. Das kann bei jedem Frame einer Animation oder bei jeder Aktualisierung der Liste der Fall sein. In beiden Fällen wird in der Benutzeroberfläche die falsche Anzahl angezeigt. Aus diesem Grund werden solche Schreibvorgänge in Compose nicht unterstützt. Durch das Verhindern dieser Schreibvorgänge kann das Framework Threads ändern, um zusammensetzbare Lambdas auszuführen.
Composable-Funktionen können in beliebiger Reihenfolge ausgeführt werden
Wenn Sie sich den Code für eine zusammensetzbare Funktion ansehen, gehen Sie möglicherweise davon aus, dass der Code in der Reihenfolge ausgeführt wird, in der er angezeigt wird. Das ist aber nicht garantiert. Wenn eine zusammensetzbare Funktion Aufrufe anderer zusammensetzbarer Funktionen enthält, werden diese Funktionen möglicherweise in beliebiger Reihenfolge ausgeführt. Compose kann erkennen, dass einige UI-Elemente eine höhere Priorität haben als andere, und sie zuerst zeichnen.
Angenommen, Sie haben Code wie diesen, um drei Bildschirme in einem Tab-Layout zu zeichnen:
@Composable fun ButtonRow() { MyFancyNavigation { StartScreen() MiddleScreen() EndScreen() } }
Die Aufrufe von StartScreen
, MiddleScreen
und EndScreen
können in beliebiger Reihenfolge erfolgen. Das bedeutet, dass StartScreen()
beispielsweise keine globale Variable festlegen kann (Nebeneffekt), die von MiddleScreen()
genutzt werden kann. Stattdessen muss jede dieser Funktionen in sich geschlossen sein.
Weitere Informationen
Weitere Informationen zu Compose und zusammensetzbaren Funktionen finden Sie in den folgenden zusätzlichen Ressourcen.
Videos
Empfehlungen für Sie
- Hinweis: Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Kotlin für Jetpack Compose
- Zustand und Jetpack Compose
- Architekturschichten in Jetpack Compose