Composition — je ústřední proces v Jetpack Compose, během kterého se z popisných Composable funkcí buduje živý UI strom zobrazovaný na obrazovce. Na rozdíl od systému View v Androidu, kde se rozvržení načítalo z XML a převádělo na neměnné objekty, Composition funguje jako dynamický systém: funkce se provádějí, vytvářejí sloty v paměti, tvoří hierarchii uzlů a propojují ji se stavem. Podle Google Android Developers, 2026 je porozumění Composition kriticky důležité pro optimalizaci výkonu Compose aplikací.
Hlavní body
Composition — je proces provádění Composable funkcí, jehož výsledkem je vnitřní reprezentace uživatelského rozhraní ve formě stromu uzlů. Každý uzel tohoto stromu odpovídá buď vestavěné komponentě (Text, Button, Image), nebo volání vlastní Composable funkce. Composition přímo nevytváří Android View objekty — buduje abstraktní popis, který je poté zpracováván fázemi Layout a Drawing.
Klíčovou vlastností Composition je jeho znovuspuštění (restartability). Každá Composable funkce v rámci kompozice může být kdykoli znovu spuštěna, pokud se změní její vstupní parametry nebo stavové objekty, které přečetla. Systém nespouští znovu celý strom — pouze ty funkce, které skutečně závisí na změněných datech.
Technicky je Composition řízen prostřednictvím Composer — interního enginu, který Kotlin kompilátor vkládá do každé Composable funkce. Composer zapisuje do slotů (skupin pozic) informace o tom, které funkce byly volány, s jakými parametry a v jakém pořadí. Při následujících voláních Composer porovnává nová data s uloženými a rozhoduje o znovuspuštění.
Proces budování UI stromu začíná voláním metody setContent uvnitř Activity nebo Fragment. Tato metoda vytváří počáteční Composition a spouští provádění kořenové Composable funkce. Poté každá vnořená Composable funkce přidává své uzly do stromu, čímž vytváří hierarchii: Row obsahuje Text a Button, Column obsahuje Image a Card, a tak dále.
Každý uzel stromu získává unikátní klíč pozice založený na jeho poloze ve zdrojovém kódu. Tento klíč se používá k identifikaci uzlu při opakovaných prováděních. Klíč pozice je důvodem, proč by pořadí volání Composable funkcí nemělo záviset na podmínkách: pokud v jednom provedení bylo voláno A -> B a v následujícím B -> A, Compose nebude schopen přiřadit staré a nové uzly.
@Composable
fun AppScreen() {
Column { // Uzel Column (pozice 1)
HeaderSection() // Uzel HeaderSection (pozice 2)
ContentSection() // Uzel ContentSection (pozice 3)
FooterSection() // Uzel FooterSection (pozice 4)
}
}
@Composable
fun HeaderSection() {
Row { // Uzel Row (pozice 2.1)
Text("Název") // Uzel Text (pozice 2.2)
Icon(...) // Uzel Icon (pozice 2.3)
}
}
V tomto příkladu každé volání získává pozici založenou na pořadí v kódu. Column (pozice 1) obsahuje tři podřízené uzly (pozice 2, 3, 4). HeaderSection přidává další dva podřízené uzly (2.1, 2.2, 2.3). Pokud je v následující rekompozici ContentSection volán před HeaderSection, Composer nebude schopen správně přiřadit uzly — odtud pravidlo: pořadí volání Composable funkcí musí být stabilní.
Stav v Composition je spravován prostřednictvím objektů typu State<T>. Když Composable funkce čte hodnotu ze State pomocí delegované vlastnosti (by), registruje závislost na tomto State. Při změně hodnoty jsou všechny funkce, které tento State přečetly, označeny pro znovuspuštění v další fázi kompozice.
Mechanismus registrace závislostí se nazývá snapshot systém. Pokaždé, když se State změní, snapshot zaznamená všechny změny a upozorní Composer, které funkce na tomto State závisí. Je důležité pochopit: čtení State v ne-Composable kódu (např. v lambdě onClick) neregistruje závislost — pouze čtení uvnitř Composable funkce nebo v lambdách provedených v kontextu kompozice.
Snapshot systém pracuje transakčně: několik změn State v rámci jedné události se sloučí do jedné transakce, což zabraňuje vícenásobným rekompozicím. To je zvláště důležité při zpracování gest: během jednoho pohybu se změní několik State objektů, ale Compose provede pouze jednu rekompozici.
@Composable
fun StateExample() {
var text by remember { mutableStateOf("Hello") }
var isVisible by remember { mutableStateOf(true) }
Column {
Text(text) // registruje závislost na text
if (isVisible) { // registruje závislost na isVisible
TextField(value = text, onValueChange = { text = it })
}
Button(onClick = { isVisible = !isVisible }) {
Text(if (isVisible) "Skrýt" else "Zobrazit")
}
}
}
Změna textu vede k rekompozici pouze Column, Text a TextField. Button a podmínka isVisible zůstávají nezměněny. Taková izolace rekompozice je klíčovou výhodou Compose oproti systémům, které překreslují celou obrazovku. Každá Composable funkce sleduje pouze ty State objekty, které přímo čte.
Composition (kompozice) a Recomposition (rekompozice) — jsou dva různé režimy provádění Composable funkcí. Composition probíhá jednou při vytvoření obrazovky: systém provede všechny Composable funkce s počátečními hodnotami a vytvoří počáteční UI strom. Recomposition probíhá opakovaně při změně dat: systém znovu spouští pouze ty funkce, které závisí na změněném stavu.
Režim Composition aktivuje všechny uzly stromu, alokuje sloty pro každou funkci, registruje všechny potomky. Recomposition pracuje selektivně: Compose porovnává nové a staré hodnoty parametrů každé funkce, a pokud se nezměnily — funkce se neprovádí (skipping).
Composition a Recomposition se liší náklady. První Composition je dražší, protože vyžaduje úplné vytvoření stromu a alokaci slotů. Recomposition je levnější, zejména pokud je většina funkcí stabilních — jejich parametry se porovnávají pomocí equals a Compose přeskočí jejich volání. Pro maximální výkon je třeba usilovat o to, aby co nejméně funkcí bylo zasaženo rekompozicí.
| Vlastnost | Composition | Recomposition |
|---|---|---|
| Kdy probíhá | Jednou, při prvním zobrazení | Opakovaně, při změně dat |
| Rozsah | Celý strom | Pouze změněné funkce |
| Porovnání parametrů | Neprovádí se | Provádí se pro skipping |
| Vytváření slotů | Ano, všechny sloty se vytvářejí | Pouze pro nové uzly |
CompositionLocal — mechanismus implicitního předávání dat stromem kompozice. Řeší problém, když je parametr třeba předat přes desítky vnořených Composable funkcí, které ho přímo nepoužívají. Místo explicitního řetězce parametrů jsou data nastavena na horní úrovni a čtena v libovolné vnořené funkci pomocí CompositionLocal.current.
Téma MaterialTheme — nejznámější příklad CompositionLocal. Všechny komponenty Compose čtou barvy, typografii a tvary přes MaterialTheme.colorScheme, MaterialTheme.typography, MaterialTheme.shapes, aniž by je přijímaly přes parametry. Vývojář může vytvářet vlastní CompositionLocal pro data jako aktuální uživatel, nastavení lokalizace nebo konfiguraci obrazovky.
Důležité omezení: CompositionLocal by se neměl používat pro často se měnící data (pozice posouvání, text v poli vstupu). Komponenta čtoucí CompositionLocal se restartuje při každé změně hodnoty, proto je pro dynamická data lepší používat explicitní parametry nebo State. CompositionLocal je optimální pro konfigurační data, která se mění zřídka nebo vůbec.
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 // čtení bez explicitního parametru
AsyncImage(model = user?.avatarUrl, contentDescription = "Avatar")
}
CompositionLocalProvider vytváří oblast viditelnosti, uvnitř které LocalUser.current vrací nastavenou hodnotu. UserAvatar čte uživatele bez explicitního předávání parametru přes zprostředkující funkce. To je zvláště cenné v hlubokých hierarchiích, kde jsou data potřebná pouze v několika listových uzlech.
Často kladené otázky
Změna State během Composition naplánuje novou rekompozici, která se provede po dokončení té současné. Nedochází k zacyklení: Compose garantuje, že každá rekompozice se provádí v samostatné transakci snapshot systému.
Na moderních zařízeních trvá Composition obrazovky s 50–100 Composable funkcemi 1–5 ms. Google doporučuje vejít se do 16 ms pro snímek při 60fps. Pokud Composition překračuje tento limit, použijte LazyColumn nebo rozdělte obrazovku na menší funkce.
Přímé ruční spuštění Composition není možné — je automaticky řízeno Composer. Lze však vynutit naplánování rekompozice změnou State nebo voláním invalidate() na kořenovém kompozitu, pokud je přístup k CompositionContext.
Hierarchie View — je neměnný strom Java objektů, který se vytváří jednou. Composition — virtuální strom, který se přestavuje při každé změně dat. View uchovává svůj stav v instančních proměnných, Composition — v slotech vázaných na pozici volání funkce.
Pokud Composable funkce přestane být volána (např. podmínka if se stane false), Composition odstraní její uzel a zavolá vyčištění DisposableEffect. Při opětovném objevení (if se stane znovu true) se vytvoří nový uzel — starý se neobnovuje.
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é