Composition — a központi folyamat a Jetpack Compose-ban, melynek során a leíró Composable függvényekből egy élő UI-fa épül fel, amely a képernyőn jelenik meg. Ellentétben az Android View-rendszerével, ahol az elrendezés XML-ből töltődött be és megváltoztathatatlan objektumokká alakult, a Composition dinamikus rendszerként működik: a függvények végrehajtódnak, slotokat hoznak létre a memóriában, csomópontok hierarchiáját alkotják és összekapcsolják azt az állapottal. A Google Android Developers, 2026 szerint a Composition megértése kritikus fontosságú a Compose alkalmazások teljesítményének optimalizálásához.
Főbb pontok
Composition — a Composable függvények végrehajtásának folyamata, melynek eredményeként a felhasználói felület belső reprezentációja csomópontok fájának formájában jön létre. A fa minden csomópontja vagy egy beépített komponensnek (Text, Button, Image), vagy egy egyéni Composable függvény meghívásának felel meg. A Composition nem hoz létre közvetlenül Android View objektumokat — egy absztrakt leírást épít, amelyet aztán a Layout és Drawing fázisok dolgoznak fel.
A Composition legfontosabb jellemzője az újraindíthatóság (restartability). A kompozícióban lévő minden Composable függvény bármikor újraindítható, ha a bemeneti paraméterei vagy az általa olvasott állapotobjektumok megváltoztak. A rendszer nem indítja újra az egész fát — csak azokat a függvényeket, amelyek ténylegesen függenek a megváltozott adatoktól.
Technikailag a Composition-t a Composer kezeli — egy belső motor, amelyet a Kotlin fordító minden Composable függvénybe beágyaz. A Composer a slotokba (pozíciócsoportokba) ír információkat arról, hogy mely függvények lettek meghívva, milyen paraméterekkel és milyen sorrendben. A következő hívásoknál a Composer összehasonlítja az új adatokat a mentettekkel, és dönt az újraindításról.
A UI-fa felépítésének folyamata a setContent metódus meghívásával kezdődik az Activity vagy Fragment belsejében. Ez a metódus létrehozza a kezdeti Composition-t és elindítja a gyökér Composable függvény végrehajtását. Ezután minden beágyazott Composable függvény hozzáadja a saját csomópontjait a fához, hierarchiát alkotva: Row tartalmazza a Text és Button elemeket, Column tartalmazza az Image és Card elemeket, és így tovább.
A fa minden csomópontja egy egyedi pozíciókulcsot kap, amely a forráskódban elfoglalt helyén alapul. Ezt a kulcsot használják a csomópont azonosítására az ismételt végrehajtások során. A pozíciókulcs az oka annak, hogy a Composable függvények meghívási sorrendje nem függhet feltételektől: ha egy végrehajtásban A -> B lett meghívva, a következőben pedig B -> A, a Compose nem tudja összepárosítani a régi és új csomópontokat.
@Composable
fun AppScreen() {
Column { // Column csomópont (1. pozíció)
HeaderSection() // HeaderSection csomópont (2. pozíció)
ContentSection() // ContentSection csomópont (3. pozíció)
FooterSection() // FooterSection csomópont (4. pozíció)
}
}
@Composable
fun HeaderSection() {
Row { // Row csomópont (2.1. pozíció)
Text("Cím") // Text csomópont (2.2. pozíció)
Icon(...) // Icon csomópont (2.3. pozíció)
}
}
Ebben a példában minden hívás a kód sorrendjén alapuló pozíciót kap. A Column (1. pozíció) három gyermek csomópontot tartalmaz (2., 3., 4. pozíció). A HeaderSection további két gyermek csomópontot ad hozzá (2.1, 2.2, 2.3). Ha a következő recompozícióban a ContentSection a HeaderSection előtt kerül meghívásra, a Composer nem tudja helyesen összepárosítani a csomópontokat — innen a szabály: a Composable függvények meghívási sorrendjének stabilnak kell lennie.
Az állapot a Composition-ban State<T> típusú objektumokon keresztül kezelődik. Amikor egy Composable függvény értéket olvas a State-ből egy delegált tulajdonságon (by) keresztül, regisztrálja a függőséget ettől a State-től. Az érték megváltozásakor az összes függvény, amely ezt a State-t olvasta, az újraindításhoz van jelölve a kompozíció következő fázisában.
A függőségek regisztrálásának mechanizmusa a snapshot rendszer. Minden alkalommal, amikor a State változik, a snapshot rögzíti az összes változást és értesíti a Composer-t arról, hogy mely függvények függenek ettől a State-től. Fontos megérteni: a State olvasása nem Composable kódban (pl. onClick lambdában) nem regisztrál függőséget — csak a Composable függvényen belüli vagy a kompozíció kontextusában végrehajtott lambdákban történő olvasás.
A snapshot rendszer tranzakciósan működik: több State-változás egy eseményen belül egy tranzakcióba egyesül, megakadályozva a többszörös recompozíciókat. Ez különösen fontos a gesztusok feldolgozásánál: egy mozdulat során több State objektum változik, de a Compose csak egy recompozíciót hajt végre.
@Composable
fun StateExample() {
var text by remember { mutableStateOf("Hello") }
var isVisible by remember { mutableStateOf(true) }
Column {
Text(text) // regisztrálja a text-től való függőséget
if (isVisible) { // regisztrálja az isVisible-től való függőséget
TextField(value = text, onValueChange = { text = it })
}
Button(onClick = { isVisible = !isVisible }) {
Text(if (isVisible) "Elrejt" else "Mutat")
}
}
}
A text megváltozása csak a Column, Text és TextField recompozíciójához vezet. A Button és az isVisible feltétel változatlan marad. A recompozíció ilyen elkülönítése a Compose fő előnye a teljes képernyőt újrarajzoló rendszerekkel szemben. Minden Composable függvény csak azokat a State objektumokat követi, amelyeket közvetlenül olvas.
Composition (kompozíció) és Recomposition (rekompozíció) — a Composable függvények végrehajtásának két különböző módja. A Composition egyszer történik a képernyő létrehozásakor: a rendszer az összes Composable függvényt kezdeti értékekkel hajtja végre és felépíti a kezdeti UI-fát. A Recomposition többször történik az adatok változásakor: a rendszer csak azokat a függvényeket indítja újra, amelyek a megváltozott állapottól függenek.
A Composition mód aktiválja a fa összes csomópontját, slotokat allokál minden függvényhez, regisztrálja az összes leszármazottat. A Recomposition szelektíven működik: a Compose összehasonlítja az egyes függvények paramétereinek új és régi értékeit, és ha nem változtak — a függvény nem kerül végrehajtásra (skipping).
A Composition és a Recomposition költségben különbözik. Az első Composition drágább, mert a fa teljes felépítését és a slotok allokálását igényli. A Recomposition olcsóbb, különösen ha a függvények többsége stabil — paramétereiket equals segítségével hasonlítják össze, és a Compose kihagyja a hívásukat. A maximális teljesítmény érdekében arra kell törekedni, hogy minél kevesebb függvényt érintsen a recompozíció.
| Jellemző | Composition | Recomposition |
|---|---|---|
| Mikor történik | Egyszer, az első megjelenítéskor | Többször, adatváltozáskor |
| Méret | Teljes fa | Csak a megváltozott függvények |
| Paraméter összehasonlítás | Nem történik | Skipping-hez történik |
| Slotok létrehozása | Igen, minden slot létrejön | Csak új csomópontokhoz |
CompositionLocal — az adatok implicit továbbításának mechanizmusa a kompozíciós fán keresztül. Megoldja a problémát, amikor egy paramétert tucatnyi beágyazott Composable függvényen kell átadni, amelyek nem használják közvetlenül. Explicit paraméterlánc helyett az adatok a felső szinten kerülnek beállításra és bármely beágyazott függvényben olvashatók a CompositionLocal.current segítségével.
A MaterialTheme téma — a CompositionLocal legismertebb példája. A Compose összes komponense a színeket, tipográfiát és formákat a MaterialTheme.colorScheme, MaterialTheme.typography, MaterialTheme.shapes segítségével olvassa, anélkül hogy paramétereken keresztül kapná azokat. A fejlesztő létrehozhat saját CompositionLocal-okat olyan adatokhoz, mint az aktuális felhasználó, lokalizációs beállítások vagy képernyő konfiguráció.
Fontos korlátozás: a CompositionLocal nem használható gyakran változó adatokhoz (görgetési pozíció, szöveg beviteli mezőben). A CompositionLocal-t olvasó komponens minden értékváltozáskor újraindul, ezért dinamikus adatokhoz jobb explicit paramétereket vagy State-t használni. A CompositionLocal ritkán vagy soha nem változó konfigurációs adatokhoz optimális.
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 // olvasás explicit paraméter nélkül
AsyncImage(model = user?.avatarUrl, contentDescription = "Avatar")
}
A CompositionLocalProvider létrehoz egy láthatósági tartományt, amelyen belül a LocalUser.current a beállított értéket adja vissza. A UserAvatar a felhasználót explicit paraméterátadás nélkül olvassa a köztes függvényeken keresztül. Ez különösen értékes mély hierarchiákban, ahol az adatok csak néhány levél csomópontban szükségesek.
Gyakran ismételt kérdések
A State megváltozása a Composition alatt egy új recompozíciót ütemez, amely a jelenlegi befejezése után hajtódik végre. Nem keletkezik hurok: a Compose garantálja, hogy minden recompozíció a snapshot rendszer külön tranzakciójában hajtódik végre.
Modern eszközökön egy 50–100 Composable függvényt tartalmazó képernyő Composition-ja 1–5 ms-ig tart. A Google azt javasolja, hogy 60fps esetén 16 ms-on belül maradjon. Ha a Composition meghaladja ezt a határt, használja a LazyColumn-t vagy ossza fel a képernyőt kisebb függvényekre.
A Composition közvetlen manuális indítása nem lehetséges — azt a Composer automatikusan kezeli. Azonban a recompozíció kényszeríthető a State megváltoztatásával vagy az invalidate() meghívásával a gyökérelemen, ha rendelkezésre áll a CompositionContext.
A View hierarchia — egy megváltoztathatatlan Java objektumokból álló fa, amely egyszer jön létre. A Composition — egy virtuális fa, amely minden adatváltozáskor újraépül. A View az állapotát példányváltozókban tárolja, a Composition — a függvény hívási pozíciójához kötött slotokban.
Ha egy Composable függvény már nem kerül meghívásra (pl. az if feltétel false lesz), a Composition eltávolítja a csomópontját és meghívja a DisposableEffect tisztítását. Újbóli megjelenéskor (if újra true) egy új csomópont jön létre — a régi nem áll helyre.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is