Composition — är den centrala processen i Jetpack Compose, under vilken ett levande UI-träd som visas på skärmen byggs från beskrivande Composable-funktioner. Till skillnad från View-systemet i Android, där layouten laddades från XML och omvandlades till oföränderliga objekt, fungerar Composition som ett dynamiskt system: funktioner exekveras, skapar slots i minnet, formar en hierarki av noder och kopplar den till tillstånd. Enligt Google Android Developers, 2026 är förståelse av Composition avgörande för att optimera prestandan hos Compose-applikationer.
Huvudpunkter
Composition — är processen att exekvera Composable-funktioner, vilket resulterar i en intern representation av användargränssnittet i form av ett träd av noder. Varje nod i detta träd motsvarar antingen en inbyggd komponent (Text, Button, Image) eller ett anrop till en anpassad Composable-funktion. Composition skapar inte direkt Android View-objekt — den bygger en abstrakt beskrivning som sedan bearbetas av faserna Layout och Drawing.
Nyckelegenskapen hos Composition är dess omstartbarhet (restartability). Varje Composable-funktion i kompositionen kan när som helst startas om om dess indataparametrar eller de tillståndsobjekt den läst har ändrats. Systemet startar inte om hela trädet — bara de funktioner som faktiskt är beroende av de ändrade data.
Tekniskt sett hanteras Composition via Composer — en intern motor som Kotlin-kompilatorn bäddar in i varje Composable-funktion. Composer skriver i slots (positionsgrupper) information om vilka funktioner som anropats, med vilka parametrar och i vilken ordning. Vid efterföljande anrop jämför Composer de nya data med de sparade och beslutar om omstart.
Processen att bygga UI-trädet börjar med anropet av metoden setContent inuti Activity eller Fragment. Denna metod skapar den initiala Composition och startar exekveringen av rot-Composable-funktionen. Därefter lägger varje nästlad Composable-funktion till sina noder i trädet, vilket bildar en hierarki: Row innehåller Text och Button, Column innehåller Image och Card, och så vidare.
Varje nod i trädet får en unik positionsnyckel baserad på dess position i källkoden. Denna nyckel används för att identifiera noden vid upprepade exekveringar. Positionsnyckeln är anledningen till att ordningen för anrop av Composable-funktioner inte bör bero på villkor: om A -> B anropades i en exekvering och B -> A i nästa, kommer Compose inte att kunna matcha gamla och nya noder.
@Composable
fun AppScreen() {
Column { // Column-nod (position 1)
HeaderSection() // HeaderSection-nod (position 2)
ContentSection() // ContentSection-nod (position 3)
FooterSection() // FooterSection-nod (position 4)
}
}
@Composable
fun HeaderSection() {
Row { // Row-nod (position 2.1)
Text("Titel") // Text-nod (position 2.2)
Icon(...) // Icon-nod (position 2.3)
}
}
I detta exempel får varje anrop en position baserad på ordningen i koden. Column (position 1) innehåller tre underordnade noder (position 2, 3, 4). HeaderSection lägger till ytterligare två underordnade noder (2.1, 2.2, 2.3). Om ContentSection i nästa recomposition anropas före HeaderSection, kommer Composer inte att kunna matcha noderna korrekt — därav regeln: ordningen för anrop av Composable-funktioner måste vara stabil.
Tillstånd i Composition hanteras via objekt av typen State<T>. När en Composable-funktion läser ett värde från State via en delegerad egenskap (by), registrerar den ett beroende av den State. När värdet ändras markeras alla funktioner som läst denna State för omstart i nästa kompositionsfas.
Mekanismen för att registrera beroenden kallas snapshot-systemet. Varje gång State ändras, registrerar snapshot alla ändringar och meddelar Composer vilka funktioner som är beroende av denna State. Det är viktigt att förstå: läsning av State i icke-Composable-kod (t.ex. i en onClick-lambda) registrerar inget beroende — endast läsning inuti en Composable-funktion eller i lambdas som exekveras i kompositionskontexten.
Snapshot-systemet fungerar transaktionsmässigt: flera State-ändringar inom en händelse slås samman till en transaktion, vilket förhindrar flera recompositioner. Detta är särskilt viktigt vid bearbetning av gester: under en rörelse ändras flera State-objekt, men Compose utför endast en recomposition.
@Composable
fun StateExample() {
var text by remember { mutableStateOf("Hello") }
var isVisible by remember { mutableStateOf(true) }
Column {
Text(text) // registrerar beroende av text
if (isVisible) { // registrerar beroende av isVisible
TextField(value = text, onValueChange = { text = it })
}
Button(onClick = { isVisible = !isVisible }) {
Text(if (isVisible) "Dölj" else "Visa")
}
}
}
Ändring av text leder till recomposition av endast Column, Text och TextField. Button och villkoret isVisible förblir oförändrade. En sådan isolering av recomposition är en viktig fördel för Compose jämfört med system som ritar om hela skärmen. Varje Composable-funktion följer endast de State-objekt som den direkt läser.
Composition (komposition) och Recomposition (rekomposition) — är två olika lägen för exekvering av Composable-funktioner. Composition sker en gång vid skapandet av skärmen: systemet exekverar alla Composable-funktioner med initialvärden och bygger det initiala UI-trädet. Recomposition sker flera gånger vid dataändring: systemet startar om endast de funktioner som är beroende av det ändrade tillståndet.
Läget Composition aktiverar alla noder i trädet, allokerar slots för varje funktion, registrerar alla avkomlingar. Recomposition arbetar selektivt: Compose jämför nya och gamla parametervärden för varje funktion, och om de inte har ändrats — exekveras funktionen inte (skipping).
Composition och Recomposition skiljer sig i kostnad. Den första Composition är dyrare eftersom den kräver fullständig uppbyggnad av trädet och allokering av slots. Recomposition är billigare, särskilt om de flesta funktioner är stabila — deras parametrar jämförs med equals, och Compose hoppar över deras anrop. För maximal prestanda bör man sträva efter att så få funktioner som möjligt påverkas av recomposition.
| Egenskap | Composition | Recomposition |
|---|---|---|
| När sker | En gång, vid första visningen | Flera gånger, vid dataändring |
| Omfattning | Hela trädet | Endast ändrade funktioner |
| Parameterjämförelse | Utförs inte | Utförs för skipping |
| Skapande av slots | Ja, alla slots skapas | Endast för nya noder |
CompositionLocal — en mekanism för implicit överföring av data genom kompositionsträdet. Den löser problemet när en parameter måste skickas genom dussintals nästlade Composable-funktioner som inte direkt använder den. Istället för en explicit parameterkedja ställs data in på den övre nivån och läses i vilken nästlad funktion som helst via CompositionLocal.current.
MaterialTheme-temat — det mest kända exemplet på CompositionLocal. Alla Compose-komponenter läser färger, typografi och former via MaterialTheme.colorScheme, MaterialTheme.typography, MaterialTheme.shapes, utan att ta emot dem via parametrar. Utvecklaren kan skapa egna CompositionLocal för data som aktuell användare, lokaliseringsinställningar eller skärmkonfiguration.
Viktig begränsning: CompositionLocal bör inte användas för ofta föränderliga data (scrollposition, text i inmatningsfält). Komponenten som läser CompositionLocal startas om vid varje värdeändring, därför är det bättre att använda explicita parametrar eller State för dynamiska data. CompositionLocal är optimal för konfigurationsdata som ändras sällan eller aldrig.
val LocalUser = compositionLocalOf<User?> { null }
@Composable
fun AppRoot(user: User, content: @Composable () -> Unit) {
CompositionLocalProvider(LocalUser.provides(user)) {
content()
}
}
@Composable
fun UserAvatar() {
val user = LocalUser.current // läsning utan explicit parameter
AsyncImage(model = user?.avatarUrl, contentDescription = "Avatar")
}
CompositionLocalProvider skapar ett synlighetsområde inom vilket LocalUser.current returnerar det inställda värdet. UserAvatar läser användaren utan explicit parameteröverföring via mellanliggande funktioner. Detta är särskilt värdefullt i djupa hierarkier där data endast behövs i ett fåtal lövnoder.
Vanliga frågor
Ändring av State under Composition schemalägger en ny recomposition som exekveras efter att den aktuella slutförts. Ingen loop uppstår: Compose garanterar att varje recomposition exekveras i en separat transaktion av snapshot-systemet.
På moderna enheter tar Composition av en skärm med 50–100 Composable-funktioner 1–5 ms. Google rekommenderar att hålla sig inom 16 ms per bildruta vid 60fps. Om Composition överskrider denna gräns, använd LazyColumn eller dela upp skärmen i mindre funktioner.
Direkt manuell start av Composition är inte möjlig — den hanteras automatiskt av Composer. Man kan dock tvinga fram en recomposition genom att ändra State eller anropa invalidate() på rotkompositen, om åtkomst till CompositionContext finns.
View-hierarkin — är ett oföränderligt träd av Java-objekt som skapas en gång. Composition — ett virtuellt träd som byggs om vid varje dataändring. View lagrar sitt tillstånd i instansvariabler, Composition — i slots bundna till funktionens anropsposition.
Om en Composable-funktion slutar anropas (t.ex. villkoret if blir false), tar Composition bort dess nod och anropar rensning av DisposableEffect. Vid återkomst (if blir true igen) skapas en ny nod — den gamla återställs inte.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också