Recomposition a Jetpack Compose mechanizmusa, amely automatikusan átépíti a felhasználói felület részeit adatváltozás esetén, a View elemek manuális frissítése nélkül. Amikor egy állapotváltozó, amelytől egy Composable függvény függ, megváltoztatja az értékét, a Compose csak azt a függvényt indítja újra, a UI-fa többi részét érintetlenül hagyva. A Google Android Developers, 2026 adatai szerint a Recomposition helyes megértése lehetővé teszi a felesleges újrarajzolások számának 40–60%-os csökkentését.
Főbb pontok
Recomposition a már Composition-ban részt vett Composable függvények újravégrehajtása a paraméterek vagy állapot új értékeivel. Az újrakomponálás fő célja a UI-fa szinkronizálása az aktuális adatokkal anélkül, hogy a teljes felületet a nulláról építené újra. Az egyszeri Composition-tól eltérően a Recomposition több százszor is elindulhat egy képernyő élettartama során.
A Recomposition a smart invalidation elve alapján működik: a Compose nyomon követi, hogy mely State objektumokat olvassa az egyes Composable függvények, és csak azokat jelöli meg újraindításra, amelyek függőségei megváltoztak. Ezt a snapshot-rendszer teszi lehetővé, amely rögzíti az összes State-olvasási műveletet a végrehajtás során, és a Composer, amely ezeket a függőségeket konkrét függvényekhez rendeli.
Fontos megérteni: az újrakomponálás nem jelenti a képernyő azonnali újrarajzolását. A Compose három fázisban működik: Composition (a UI leírás felépítése), Layout (méretek és pozíciók kiszámítása) és Drawing (rajzolás a vászonra). Ha az újrakomponálás után az elemek méretei és pozíciói nem változtak, a Layout fázis kihagyható. Ha a megjelenés nem változott — a Drawing kimarad. Ez a háromfázisú architektúra biztosítja az egyes UI-frissítések minimális költségét.
Az újrakomponálásnak három fő triggere van. Az első — a Composable függvény törzsében olvasott State objektum megváltozása. Amikor a mutableStateOf vagy derivedStateOf megváltoztatja az értékét, az összes függvény, amely regisztrálta ennek a State-nek az olvasását az előző kompozícióban, újraindításra kerül megjelölésre.
A második trigger — a paraméterek megváltozása a Composable függvény szülő függvényből történő meghívásakor. Ha a szülő függvény új értéket adott át (pl. a szöveg vagy szám megváltozott), a gyermek függvény újraindul, még akkor is, ha nem olvas State-et magában. A Compose összehasonlítja a paraméterek új és régi értékeit equals segítségével, és ha egyenlők — a függvény kihagyható.
A harmadik trigger — a CompositionLocal megváltozása a CompositionLocalProvider segítségével. Az összes függvény, amely .current-en keresztül olvassa a CompositionLocal-t, újraindul a provider változásakor. Ezt a mechanizmust használja a MaterialTheme: a téma váltása (világos/sötét) az összes olyan komponens újrakomponálását okozza, amely olvassa a MaterialTheme.colorScheme-t.
@Composable
fun RecompositionDemo() {
var counter by remember { mutableStateOf(0) }
var text by remember { mutableStateOf("Hello") }
Column {
Text("Számláló: $counter") // újrakomponálás a számláló változásakor
Text("Üzenet: $text") // újrakomponálás a szöveg változásakor
Button(onClick = { counter++ }) {
Text("+1")
}
Button(onClick = { text = "Világ" }) {
Text("Szöveg módosítása")
}
}
}
A +1 gomb megnyomása megváltoztatja a counter-t, ami csak az első Text sor és maga a Column újrakomponálását okozza. A második Text sor, amely a text-et jeleníti meg, nem indul újra. Az ilyen elkülönítés a snapshot-rendszer eredménye: minden Composable függvény csak azokról a State objektumokról tud, amelyeket elolvasott.
Az újrakomponálás optimalizálása az adatstruktúrák helyes megválasztásával kezdődik. Használjon megváltoztathatatlan gyűjteményeket (listOf, mapOf) a változtathatók (mutableListOf) helyett. A Compose equals segítségével hasonlítja össze a paramétereket, és ha a gyűjtemény megváltozott, de az equals true-t adott vissza — a függvény nem indul újra. Változtatható gyűjteményekhez használja a SnapshotStateList-et, amely helyesen követi a változásokat elemszinten.
A második technika — a stabil részek kiemelése a UI-ból külön Composable függvényekbe. Ha a képernyő egy része nem függ a gyakran változó állapottól, emelje ki egy külön függvénybe paraméterekkel. Amikor újrakomponálás történik, a stabil függvény ugyanazokat a paramétereket kapja, a Compose összehasonlítja őket és kihagyja a végrehajtást. Ez előnyösebb, mint ezt a részt egy nagy függvény részeként újraindítani, ahol a paraméterek egy része megváltozott.
A harmadik technika — kulcsok a LazyColumn-ban. Mindig adjon meg key-t az item-hez a LazyColumn, LazyGrid és más lusta tárolókban. A kulcs lehetővé teszi a Compose számára az elemek azonosítását a lista változásakor: hozzáadás, törlés vagy átrendezés. Kulcs nélkül a Compose a lista összes elemét újraindítja minden változásnál, ami nagy listáknál észrevehető teljesítménycsökkenést okoz.
// Optimalizált struktúra: stabil részek külön kiemelve
@Composable
fun OptimizedScreen(items: List<Item>) {
Column {
Header() // nem függ az elemektől — nincs újrakomponálás
Spacer(modifier = Modifier.height(8.dp))
LazyColumn {
items(items, key = { it.id }) { item ->
ItemRow(item = item) // újrakomponálás csak a megváltozott elemekhez
}
}
}
}
@Composable
fun Header() {
Text("Elemlista", style = MaterialTheme.typography.headlineMedium)
}
@Composable
fun ItemRow(item: Item) {
Text(item.title)
}
Skipping egy mechanizmus, amelyben a Compose kihagyja egy Composable függvény végrehajtását, ha az összes paramétere nem változott. A skipping helyes működéséhez a paramétertípusoknak stabilnak (stable) kell lenniük. A Kotlin fordító stabilként jelöli meg: a primitív típusokat (Int, Float, Boolean), String-et, lambda függvényeket, valamint azokat az osztályokat, amelyeknek minden mezője stabil és val.
Stability egy @Stable vagy @Immutable annotáció, amely egyéni adatosztályokhoz adható hozzá. Ha egy osztály változtatható mezőt (var) tartalmaz, a fordító instabilnak tekinti, és a Compose nem tudja kihagyni az ilyen paraméterekkel rendelkező függvényeket. A var-osztályokhoz használja a @Stable-t, ha garantálja, hogy a változásról szóló értesítés a snapshot-rendszeren keresztül kerül elküldésre.
A Stability a fordítói flag segítségével ellenőrizhető: -P "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=. /reports". Ez egy jelentést generál az összes Composable függvény és paramétereik listájával, stability jelzéssel. Ha egy paraméter instabil — a skipping lehetetlen, és a függvény minden szülő újrakomponálásakor újraindul.
| Típus | Stabilitás | Skipping |
|---|---|---|
| Int, Float, Boolean | Stabil | Igen |
| String | Stabil | Igen |
| Lambda | Stabil | Igen |
| data class val mezőkkel | Stabil | Igen |
| data class var mezőkkel | Instabil | Nem |
| List<String> | Instabil | Nem |
Figyelje meg: a List<String> instabilnak számít, mert ez egy interfész, nem konkrét implementáció. Használja az immutableListOf()-ot a Kotlin Collections Immutable könyvtárból, vagy csomagolja a listát egy @Stable osztályba. A Lambda mindig stabil, mivel az equals csak referenciákat hasonlít össze, és egy új lambda létrehozásakor a hívás helyén a szülő függvény is újraindul.
Az újrakomponálások figyeléséhez az Android Studio Layout Inspector-t biztosít Compose Recomposition Counts móddal. Ebben a módban minden Composable függvény megjeleníti az újrakomponálások számát és az újraindítás okait. Ez lehetővé teszi a túl gyakran újrakomponált függvények gyors megtalálását és a kiváltó ok meghatározását — instabil paraméterek vagy felesleges State-függőségek.
További eszközök: Compose Metrics (statisztikák gyűjtése instrumentation teszteken keresztül) és Recomposition Timer (az egyes függvények végrehajtási idejének mérése). A Google azt javasolja, hogy ezeket az eszközöket a profilozási fázisban kapcsolja be, és a kiadás build-ekben kapcsolja ki, mivel akár 20% többletterhelést is adnak minden újrakomponáláshoz.
Az újrakomponálások elemzésekor keresse az unnecessary recomposition mintákat: a függvény újraindul, holott a kimeneti UI-nak nem kellene változnia. Gyakori ok a lambda remember nélküli használata, amikor minden alkalommal új lambda objektum jön létre, és a Compose a paramétert megváltozottnak tekinti. Megoldás: a lambdák becsomagolása remember { }-be fix capture-ökkel.
// Rossz: új lambda minden szülő újrakomponáláskor
@Composable
fun Parent() {
Child(onClick = { doSomething() }) // minden alkalommal új lambda
}
// Jó: a remember stabilizálja a lambdát
@Composable
fun Parent() {
val onClick = remember { { doSomething() } }
Child(onClick = onClick) // ugyanaz a referencia
}
Gyakran ismételt kérdések
Nem, az újrakomponálás csak a Composition fázis. Ezt követi a Layout és a Drawing. Ha az újrakomponálás után az elemek méretei és pozíciói nem változtak, a Layout és Drawing teljesen kihagyható, ami GPU-erőforrásokat takarít meg.
Animációknál az újrakomponálás akár 120-szor is elindulhat másodpercenként (120fps). Normál interakció esetén — 10–60-szor másodpercenként. Fontos, hogy minden újrakomponálás beleférjen a képkocka keretébe (8–16 ms), különben az alkalmazás lassulni fog.
Az ok — a paraméter megváltozása a szülő függvénytől. A szülő újraindul (saját okból) és új értéket ad át. Ennek elkerüléséhez ellenőrizze a paraméterek stability-jét és használja a remember-t a lambdák és számított értékek stabilizálásához.
Közvetlen kikapcsolás nincs, de létezik kényszerített skipping a readInComposition segítségével — a State a függvény törzsén kívül kerül olvasásra, ami nem regisztrál függőséget. Használja ezt óvatosan: a függvény nem fog reagálni a változásokra, ami elavult UI-hoz vezethet.
A Composition drágább, mert az összes slotot és fa-csomópontot a nulláról hozza létre. A Recomposition újrahasznosítja a meglévő slotokat, és csak azok értékeit frissíti. A gyakorlatban egy képernyő Composition-ja 2–10 ms-ot vesz igénybe, egy elem újrakomponálása pedig 0.1–1 ms-ot.
Ö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