Recomposition je mechanismus Jetpack Compose, který automaticky přestavuje části uživatelského rozhraní při změně dat, bez ruční aktualizace View elementů. Když proměnná stavu, na které závisí Composable funkce, změní hodnotu, Compose restartuje pouze tuto funkci, zatímco zbytek UI stromu zůstává nedotčen. Podle údajů Google Android Developers, 2026, správné pochopení Recomposition umožňuje snížit počet zbytečných překreslení o 40–60%.
Hlavní body
Recomposition je opětovné provedení Composable funkcí, které se již účastnily Composition, s novými hodnotami parametrů nebo stavu. Hlavním cílem rekompozice je synchronizace UI stromu s aktuálními daty bez přestavby celého rozhraní od nuly. Na rozdíl od Composition, který probíhá jednorázově, Recomposition může být spuštěna stokrát během životnosti obrazovky.
Recomposition funguje na principu smart invalidation: Compose sleduje, které State objekty každá Composable funkce čte, a označuje k restartování pouze ty, jejichž závislosti se změnily. Toho je dosaženo pomocí snapshot systému, který zaznamenává všechny operace čtení State během provádění, a Composer, který tyto závislosti mapuje na konkrétní funkce.
Je důležité pochopit: rekompozice neznamená okamžité překreslení obrazovky. Compose pracuje ve třech fázích: Composition (budování popisu UI), Layout (výpočet velikostí a pozic) a Drawing (kreslení na plátno). Pokud se po rekompozici velikosti a poloha prvků nezměnily, fáze Layout může být přeskočena. Pokud se vzhled nezměnil — Drawing je přeskočen. Tato třífázová architektura zajišťuje minimální náklady každé aktualizace UI.
Existují tři hlavní spouštěče rekompozice. První — změna State objektu čteného v těle Composable funkce. Když mutableStateOf nebo derivedStateOf změní svou hodnotu, všechny funkce, které zaregistrovaly čtení tohoto State v předchozí kompozici, jsou označeny k restartování.
Druhý spouštěč — změna parametrů Composable funkce při jejím volání z rodičovské funkce. Pokud rodičovská funkce předala novou hodnotu (např. změnil se text nebo číslo), podřízená funkce bude restartována, i když v sobě nečte State. Compose porovnává nové a staré hodnoty parametrů pomocí equals, a pokud jsou stejné — funkce může být přeskočena.
Třetí spouštěč — změna CompositionLocal prostřednictvím CompositionLocalProvider. Všechny funkce, které čtou CompositionLocal pomocí .current, jsou restartovány při změně provideru. Tento mechanismus používá MaterialTheme: změna motivu (světlý/tmavý) způsobuje rekompozici všech komponent, které čtou MaterialTheme.colorScheme.
@Composable
fun RecompositionDemo() {
var counter by remember { mutableStateOf(0) }
var text by remember { mutableStateOf("Hello") }
Column {
Text("Čítač: $counter") // rekompozice při změně čítače
Text("Zpráva: $text") // rekompozice při změně textu
Button(onClick = { counter++ }) {
Text("+1")
}
Button(onClick = { text = "Svět" }) {
Text("Změnit text")
}
}
}
Stisknutí tlačítka +1 mění counter, což způsobuje rekompozici pouze prvního řádku Text a samotné Column. Druhý řádek Text, který zobrazuje text, není restartován. Taková izolace — výsledek snapshot systému: každá Composable funkce ví jen o těch State objektech, které přečetla.
Optimalizace rekompozice začíná správným výběrem datových struktur. Používejte neměnné kolekce (listOf, mapOf) místo měnitelných (mutableListOf). Compose porovnává parametry pomocí equals, a pokud se kolekce změnila, ale equals vrátil true — funkce nebude restartována. Pro měnitelné kolekce používejte SnapshotStateList, který implementuje správné sledování změn na úrovni prvků.
Druhá technika — vyčlenění stabilních částí UI do samostatných Composable funkcí. Pokud část obrazovky nezávisí na často se měnícím stavu, vyčleňte ji do samostatné funkce s parametry. Když přijde rekompozice, stabilní funkce obdrží stejné parametry, Compose je porovná a přeskočí provedení. To je výhodnější než restartovat tuto část v rámci velké funkce, kde se část parametrů změnila.
Třetí technika — klíče v LazyColumn. Vždy uvádějte key pro item v LazyColumn, LazyGrid a dalších líných kontejnerech. Klíč umožňuje Compose identifikovat prvky při změně seznamu: přidání, odstranění nebo přeskupení. Bez klíče Compose restartuje všechny prvky seznamu při každé změně, což na velkých seznamech způsobuje znatelnou ztrátu výkonu.
// Optimalizovaná struktura: stabilní části vyčleněny samostatně
@Composable
fun OptimizedScreen(items: List<Item>) {
Column {
Header() // nezávisí na položkách — žádná rekompozice
Spacer(modifier = Modifier.height(8.dp))
LazyColumn {
items(items, key = { it.id }) { item ->
ItemRow(item = item) // rekompozice pouze pro změněné položky
}
}
}
}
@Composable
fun Header() {
Text("Seznam položek", style = MaterialTheme.typography.headlineMedium)
}
@Composable
fun ItemRow(item: Item) {
Text(item.title)
}
Skipping je mechanismus, při kterém Compose přeskočí provedení Composable funkce, pokud se všechny její parametry nezměnily. Aby skipping fungoval správně, typy parametrů musí být stabilní (stable). Kotlin kompilátor označuje jako stabilní: primitivní typy (Int, Float, Boolean), String, lambda funkce, stejně jako třídy, jejichž všechna pole jsou stabilní a val.
Stability je anotace @Stable nebo @Immutable, kterou lze přidat vlastním datovým třídám. Pokud třída obsahuje měnitelné pole (var), kompilátor ji považuje za nestabilní a Compose nemůže přeskočit funkce s takovými parametry. Pro třídy s var použijte @Stable, pokud garantujete, že oznámení o změně bude odesláno prostřednictvím snapshot systému.
Stability lze zkontrolovat pomocí přepínače kompilátoru -P "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=. /reports". Ten generuje zprávu se seznamem všech Composable funkcí a jejich parametrů s označením stability. Pokud je parametr nestabilní — skipping pro tuto funkci není možný a bude restartována při každé rekompozici rodiče.
| Typ | Stabilita | Skipping |
|---|---|---|
| Int, Float, Boolean | Stabilní | Ano |
| String | Stabilní | Ano |
| Lambda | Stabilní | Ano |
| data class s poli val | Stabilní | Ano |
| data class s poli var | Nestabilní | Ne |
| List<String> | Nestabilní | Ne |
Všimněte si: List<String> je považována za nestabilní, protože je to rozhraní, nikoli konkrétní implementace. Používejte immutableListOf() z knihovny Kotlin Collections Immutable nebo zabalte seznam do @Stable třídy. Lambda je vždy stabilní, protože její equals porovnává pouze reference, a při vytvoření nové lambdy v místě volání je rodičovská funkce také restartována.
Pro sledování rekompozic poskytuje Android Studio Layout Inspector s režimem Compose Recomposition Counts. V tomto režimu každá Composable funkce zobrazuje počet rekompozic a důvody restartování. To umožňuje rychle najít funkce, které jsou rekomponovány příliš často, a určit hlavní příčinu — nestabilní parametry nebo zbytečné State závislosti.
Další nástroje: Compose Metrics (sběr statistik pomocí instrumentačních testů) a Recomposition Timer (měření doby provedení každé funkce). Google doporučuje zapínat tyto nástroje ve fázi profilování a vypínat v release sestaveních, protože přidávají režii až 20% na každou rekompozici.
Při analýze rekompozic hledejte vzory unnecessary recomposition: funkce je restartována, ačkoli by se její výstupní UI nemělo změnit. Častou příčinou je používání lambd bez remember, kdy se pokaždé vytváří nový objekt lambdy a Compose považuje parametr za změněný. Řešení: zabalení lambd do remember { } s pevnými zachyceními.
// Špatně: nová lambda při každé rekompozici rodiče
@Composable
fun Parent() {
Child(onClick = { doSomething() }) // pokaždé nová lambda
}
// Dobře: remember stabilizuje lambdu
@Composable
fun Parent() {
val onClick = remember { { doSomething() } }
Child(onClick = onClick) // stejná reference
}
Často kladené otázky
Ne, rekompozice je pouze fáze Composition. Po ní následují Layout a Drawing. Pokud se po rekompozici velikosti a poloha prvků nezměnily, Layout a Drawing mohou být zcela přeskočeny, což šetří prostředky GPU.
Při animacích může rekompozice běžet až 120krát za sekundu (120fps). Pro běžnou interakci — 10–60krát za sekundu. Je důležité, aby každá rekompozice zapadala do rozpočtu snímku (8–16 ms), jinak bude aplikace zpomalovat.
Příčinou je změna parametru od rodičovské funkce. Rodič restartuje (z vlastního důvodu) a předá novou hodnotu. Abyste tomu předešli, zkontrolujte stability parametrů a používejte remember pro stabilizaci lambd a vypočítaných hodnot.
Přímé vypnutí neexistuje, ale existuje vynucené přeskočení pomocí readInComposition — State je čten mimo tělo funkce, což neregistruje závislost. Používejte to opatrně: funkce nebude reagovat na změny, což může vést k zastaralému UI.
Composition je dražší, protože vytváří všechny sloty a uzly stromu od nuly. Recomposition znovu používá existující sloty a pouze aktualizuje jejich hodnoty. V praxi Composition obrazovky trvá 2–10 ms a rekompozice jednoho prvku — 0.1–1 ms.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také