Composition — is het centrale proces in Jetpack Compose, waarbij uit beschrijvende Composable-functies een levende UI-boom wordt opgebouwd die op het scherm wordt weergegeven. In tegenstelling tot het View-systeem in Android, waar de layout uit XML werd geladen en omgezet in onveranderlijke objecten, werkt Composition als een dynamisch systeem: functies worden uitgevoerd, creëren slots in het geheugen, vormen een hiërarchie van knooppunten en koppelen deze aan de toestand. Volgens Google Android Developers, 2026 is het begrijpen van Composition cruciaal voor het optimaliseren van de prestaties van Compose-applicaties.
Belangrijkste punten
Composition — is het proces van het uitvoeren van Composable-functies, resulterend in een interne representatie van de gebruikersinterface in de vorm van een boom van knooppunten. Elk knooppunt van deze boom komt overeen met een ingebouwd component (Text, Button, Image) of een aanroep van een aangepaste Composable-functie. Composition maakt niet direct Android View-objecten — het bouwt een abstracte beschrijving die vervolgens wordt verwerkt door de fasen Layout en Drawing.
Het belangrijkste kenmerk van Composition is de herstartbaarheid (restartability). Elke Composable-functie in de compositie kan op elk moment opnieuw worden gestart als de invoerparameters of de doorgelezen toestandsobjecten zijn gewijzigd. Het systeem herstart niet de hele boom — alleen de functies die daadwerkelijk afhankelijk zijn van de gewijzigde gegevens.
Technisch wordt Composition beheerd via Composer — een interne engine die de Kotlin-compiler in elke Composable-functie inbouwt. Composer schrijft in slots (positiegroepen) informatie over welke functies zijn aangeroepen, met welke parameters en in welke volgorde. Bij volgende aanroepen vergelijkt Composer de nieuwe gegevens met de opgeslagen en beslist over herstart.
Het proces van het opbouwen van de UI-boom begint met het aanroepen van de methode setContent in Activity of Fragment. Deze methode creëert de initiële Composition en start de uitvoering van de root Composable-functie. Vervolgens voegt elke geneste Composable-functie zijn knooppunten toe aan de boom, waardoor een hiërarchie ontstaat: Row bevat Text en Button, Column bevat Image en Card, enzovoort.
Elk knooppunt van de boom krijgt een unieke positiesleutel, gebaseerd op zijn positie in de broncode. Deze sleutel wordt gebruikt voor identificatie van het knooppunt bij herhaalde uitvoeringen. De positiesleutel is de reden waarom de volgorde van het aanroepen van Composable-functies niet van voorwaarden mag afhangen: als in de ene uitvoering A -> B is aangeroepen en in de volgende B -> A, kan Compose de oude en nieuwe knooppunten niet matchen.
@Composable
fun AppScreen() {
Column { // Column-knooppunt (positie 1)
HeaderSection() // HeaderSection-knooppunt (positie 2)
ContentSection() // ContentSection-knooppunt (positie 3)
FooterSection() // FooterSection-knooppunt (positie 4)
}
}
@Composable
fun HeaderSection() {
Row { // Row-knooppunt (positie 2.1)
Text("Titel") // Text-knooppunt (positie 2.2)
Icon(...) // Icon-knooppunt (positie 2.3)
}
}
In dit voorbeeld krijgt elke aanroep een positie op basis van de volgorde in de code. Column (positie 1) bevat drie onderliggende knooppunten (posities 2, 3, 4). HeaderSection voegt nog twee onderliggende knooppunten toe (2.1, 2.2, 2.3). Als in de volgende recompositie ContentSection vóór HeaderSection wordt aangeroepen, kan Composer de knooppunten niet correct matchen — vandaar de regel: de volgorde van aanroepen van Composable-functies moet stabiel zijn.
Toestand in Composition wordt beheerd via objecten van het type State<T>. Wanneer een Composable-functie een waarde uit State leest via een gedelegeerde eigenschap (by), registreert deze een afhankelijkheid van die State. Bij wijziging van de waarde worden alle functies die deze State hebben gelezen, gemarkeerd voor herstart in de volgende compositiefase.
Het mechanisme voor het registreren van afhankelijkheden heet het snapshotsysteem. Elke keer dat State verandert, registreert de snapshot alle wijzigingen en stelt Composer op de hoogte van welke functies afhankelijk zijn van deze State. Het is belangrijk om te begrijpen: het lezen van State in niet-Composable-code (bijvoorbeeld in een onClick-lambda) registreert geen afhankelijkheid — alleen lezen binnen een Composable-functie of in lambda's die in de context van de compositie worden uitgevoerd.
Het snapshotsysteem werkt transactioneel: meerdere wijzigingen van State binnen één gebeurtenis worden samengevoegd tot één transactie, wat meerdere recomposities voorkomt. Dit is vooral belangrijk bij het verwerken van gebaren: tijdens één beweging veranderen meerdere State-objecten, maar Compose voert slechts één recompositie uit.
@Composable
fun StateExample() {
var text by remember { mutableStateOf("Hello") }
var isVisible by remember { mutableStateOf(true) }
Column {
Text(text) // registreert afhankelijkheid van text
if (isVisible) { // registreert afhankelijkheid van isVisible
TextField(value = text, onValueChange = { text = it })
}
Button(onClick = { isVisible = !isVisible }) {
Text(if (isVisible) "Verbergen" else "Tonen")
}
}
}
Wijziging van text leidt tot recompositie van alleen Column, Text en TextField. Button en de voorwaarde isVisible blijven ongewijzigd. Een dergelijke isolatie van recompositie is een belangrijk voordeel van Compose ten opzichte van systemen die het hele scherm opnieuw tekenen. Elke Composable-functie volgt alleen die State-objecten die hij direct leest.
Composition (compositie) en Recomposition (recompositie) — zijn twee verschillende uitvoeringsmodi van Composable-functies. Composition vindt eenmalig plaats bij het maken van het scherm: het systeem voert alle Composable-functies uit met beginwaarden en bouwt de initiële UI-boom. Recomposition vindt meerdere keren plaats bij wijziging van gegevens: het systeem herstart alleen de functies die afhankelijk zijn van de gewijzigde toestand.
Modus Composition activeert alle knooppunten van de boom, wijst slots toe voor elke functie, registreert alle afstammelingen. Recomposition werkt selectief: Composer vergelijkt nieuwe en oude parameterwaarden van elke functie, en als ze niet zijn gewijzigd — wordt de functie niet uitgevoerd (skipping).
Composition en Recomposition verschillen in kosten. De eerste Composition is duurder, omdat deze volledige opbouw van de boom en toewijzing van slots vereist. Recomposition is goedkoper, vooral als de meeste functies stabiel zijn — hun parameters worden vergeleken via equals en Compose slaat hun aanroep over. Voor maximale prestaties moet men ernaar streven dat zo min mogelijk functies door recompositie worden getroffen.
| Kenmerk | Composition | Recomposition |
|---|---|---|
| Wanneer vindt plaats | Eenmalig, bij eerste weergave | Meerdere keren, bij gegevenswijziging |
| Omvang | Hele boom | Alleen gewijzigde functies |
| Parametervergelijking | Niet uitgevoerd | Uitgevoerd voor skipping |
| Aanmaken slots | Ja, alle slots worden aangemaakt | Alleen voor nieuwe knooppunten |
CompositionLocal — een mechanisme voor het impliciet doorgeven van gegevens via de compositieboom. Het lost het probleem op wanneer een parameter moet worden doorgegeven via tientallen geneste Composable-functies die hem niet direct gebruiken. In plaats van een expliciete parameterketen worden gegevens op het hoogste niveau ingesteld en in elke geneste functie gelezen via CompositionLocal.current.
Het MaterialTheme-thema — het bekendste voorbeeld van CompositionLocal. Alle Compose-componenten lezen kleuren, typografie en vormen via MaterialTheme.colorScheme, MaterialTheme.typography, MaterialTheme.shapes, zonder ze via parameters te ontvangen. De ontwikkelaar kan eigen CompositionLocal maken voor gegevens zoals de huidige gebruiker, lokalisatie-instellingen of schermconfiguratie.
Belangrijke beperking: CompositionLocal moet niet worden gebruikt voor frequent veranderende gegevens (scrollpositie, tekst in een invoerveld). Het component dat CompositionLocal leest, wordt bij elke waardewijziging herstart, daarom is het voor dynamische gegevens beter om expliciete parameters of State te gebruiken. CompositionLocal is optimaal voor configuratiegegevens die zelden of nooit veranderen.
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 // lezen zonder expliciete parameter
AsyncImage(model = user?.avatarUrl, contentDescription = "Avatar")
}
CompositionLocalProvider creëert een zichtbaarheidsbereik waarbinnen LocalUser.current de ingestelde waarde retourneert. UserAvatar leest de gebruiker zonder expliciete parameteroverdracht via tussenliggende functies. Dit is vooral waardevol in diepe hiërarchieën waar gegevens slechts in enkele bladknooppunten nodig zijn.
Veelgestelde vragen
Wijziging van State tijdens Composition plant een nieuwe recompositie die wordt uitgevoerd nadat de huidige is voltooid. Er treedt geen lus op: Compose garandeert dat elke recompositie in een aparte transactie van het snapshotsysteem wordt uitgevoerd.
Op moderne apparaten duurt Composition van een scherm met 50–100 Composable-functies 1–5 ms. Google adviseert om binnen 16 ms per frame bij 60fps te blijven. Als Composition deze limiet overschrijdt, gebruik dan LazyColumn of verdeel het scherm in kleinere functies.
Direct handmatig starten van Composition is niet mogelijk — het wordt automatisch beheerd door Composer. Men kan echter geforceerd een recompositie plannen door State te wijzigen of invalidate() aan te roepen op de root-composite, indien toegang tot CompositionContext beschikbaar is.
View-hiërarchie — is een onveranderlijke boom van Java-objecten die eenmalig wordt gemaakt. Composition — een virtuele boom die bij elke gegevenswijziging wordt herbouwd. View behoudt zijn toestand in instantievariabelen, Composition — in slots die zijn gekoppeld aan de aanroeppositie van de functie.
Als een Composable-functie niet meer wordt aangeroepen (bijvoorbeeld de voorwaarde if wordt false), verwijdert Composition het knooppunt en roept de opschoning van DisposableEffect aan. Bij terugkeer (if wordt weer true) wordt een nieuw knooppunt aangemaakt — het oude wordt niet hersteld.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook