Composition — централни процес у Jetpack Compose-у, током којег се од описних Composable функција гради живо UI стабло које се приказује на екрану. За разлику од View система у Android-у, где се распоред учитавао из XML-а и претварао у непроменљиве објекте, Composition ради као динамички систем: функције се извршавају, стварају слотове у меморији, формирају хијерархију чворова и повезују је са стањем. Према Google Android Developers, 2026, разумевање Composition-а је критично за оптимизацију перформанси Compose апликација.
Главне тачке
Composition — процес извршавања Composable функција, чијим резултатом се формира унутрашња репрезентација корисничког интерфејса у облику стабла чворова. Сваки чвор овог стабла одговара или уграђеној компоненти (Text, Button, Image) или позиву прилагођене Composable функције. Composition не ствара директно Android View објекте — гради апстрактни опис који се затим обрађује фазама Layout и Drawing.
Кључна карактеристика Composition-а је његова могућност поновног покретања (restartability). Свака Composable функција у саставу композиције може се поново покренути у било ком тренутку, ако су се променили њени улазни параметри или објекти стања које је прочитала. Систем не покреће поново цело стабло — само оне функције које заиста зависе од промењених података.
Технички, Composition-ом управља Composer — унутрашњи мотор који Kotlin компајлер уграђује у сваку Composable функцију. Composer уписује у слотове (групе позиција) информације о томе које су функције позване, са којим параметрима и којим редоследом. При наредним позивима, Composer пореди нове податке са сачуваним и доноси одлуку о поновном покретању.
Процес изградње UI стабла почиње позивом методе setContent унутар Activity или Fragment-а. Ова метода ствара почетни Composition и покреће извршавање коренске Composable функције. Затим свака угнежђена Composable функција додаје своје чворове у стабло, формирајући хијерархију: Row садржи Text и Button, Column садржи Image и Card, и тако даље.
Сваки чвор стабла добија јединствени кључ позиције, заснован на његовом положају у изворном коду. Овај кључ се користи за идентификацију чвора при поновљеним извршавањима. Кључ позиције је разлог зашто редослед позивања Composable функција не треба да зависи од услова: ако су у једном извршавању позване A -> B, а у следећем B -> A, Compose неће моћи да упари старе и нове чворове.
@Composable
fun AppScreen() {
Column { // Чвор Column (позиција 1)
HeaderSection() // Чвор HeaderSection (позиција 2)
ContentSection() // Чвор ContentSection (позиција 3)
FooterSection() // Чвор FooterSection (позиција 4)
}
}
@Composable
fun HeaderSection() {
Row { // Чвор Row (позиција 2.1)
Text("Наслов") // Чвор Text (позиција 2.2)
Icon(...) // Чвор Icon (позиција 2.3)
}
}
У овом примеру сваки позив добија позицију засновану на редоследу у коду. Column (позиција 1) садржи три подређена чвора (позиције 2, 3, 4). HeaderSection додаје још два подређена чвора (2.1, 2.2, 2.3). Ако у следећој рекомпозицији ContentSection буде позван пре HeaderSection-а, Composer неће моћи правилно да упари чворове — отуда правило: редослед позивања Composable функција мора бити стабилан.
Стање у Composition-у се управља преко објеката типа State<T>. Када Composable функција чита вредност из State-а преко делегираног својства (by), региструје зависност од тог State-а. При промени вредности, све функције које су прочитале овај State се обележавају за поновно покретање у следећој фази композиције.
Механизам регистрације зависности назива се систем снимака (snapshot system). Сваки пут када се State промени, снимак бележи све промене и обавештава Composer које функције зависе од овог State-а. Важно је разумети: читање State-а у коду који није Composable (нпр. у ламбди onClick) не региструје зависност — само читање унутар Composable функције или у ламбдама извршеним у контексту композиције.
Систем снимака ради транзакционо: неколико промена State-а у оквиру једног догађаја обједињује се у једну транзакцију, што спречава вишеструке рекомпозиције. Ово је посебно важно при обради гестова: током једног покрета мења се неколико State објеката, али Compose извршава само једну рекомпозицију.
@Composable
fun StateExample() {
var text by remember { mutableStateOf("Hello") }
var isVisible by remember { mutableStateOf(true) }
Column {
Text(text) // региструје зависност од text
if (isVisible) { // региструје зависност од isVisible
TextField(value = text, onValueChange = { text = it })
}
Button(onClick = { isVisible = !isVisible }) {
Text(if (isVisible) "Сакриј" else "Прикажи")
}
}
}
Промена text-а доводи до рекомпозиције само Column, Text и TextField-а. Button и услов isVisible остају непромењени. Таква изолованост рекомпозиције је кључна предност Compose-а у односу на системе који прецртавају цео екран. Свака Composable функција прати само оне State објекте које директно чита.
Composition (композиција) и Recomposition (рекомпозиција) — два различита режима извршавања Composable функција. Composition се дешава једном при креирању екрана: систем извршава све Composable функције са почетним вредностима и гради почетно UI стабло. Recomposition се дешава више пута при промени података: систем поново покреће само оне функције које зависе од промењеног стања.
Режим Composition активира све чворове стабла, додељује слотове за сваку функцију, региструје све потомке. Recomposition ради селективно: Compose пореди нове и старе вредности параметара сваке функције, и ако се нису промениле — функција се не извршава (skipping).
Composition и Recomposition се разликују по цени. Први Composition је скупљи, јер захтева потпуну изградњу стабла и доделу слотова. Recomposition је јефтинији, посебно ако је већина функција стабилна — њихови параметри се пореде по equals, а Compose прескаче њихов позив. За максималне перформансе треба тежити да што мање функција буде захваћено рекомпозицијом.
| Карактеристика | Composition | Recomposition |
|---|---|---|
| Када се дешава | Једном, при првом приказивању | Више пута, при промени података |
| Обим | Цело стабло | Само измењене функције |
| Поређење параметара | Не извршава се | Извршава се за skipping |
| Креирање слотова | Да, сви слотови се креирају | Само за нове чворове |
CompositionLocal — механизам имплицитног преноса података кроз стабло композиције. Решава проблем када параметар треба пренети кроз десетине угнежђених Composable функција које га директно не користе. Уместо експлицитног ланца параметара, подаци се постављају на горњем нивоу и читају у било којој угнежђеној функцији кроз CompositionLocal.current.
Тема MaterialTheme — најпознатији пример CompositionLocal-а. Све компоненте Compose-а читају боје, типографију и облике кроз MaterialTheme.colorScheme, MaterialTheme.typography, MaterialTheme.shapes, не примајући их кроз параметре. Програмер може креирати сопствене CompositionLocal за податке као што су тренутни корисник, подешавања локализације или конфигурација екрана.
Важно ограничење: CompositionLocal не треба користити за податке који се често мењају (положај скроловања, текст у пољу за унос). Компонента која чита CompositionLocal се поново покреће при свакој промени вредности, стога је за динамичке податке боље користити експлицитне параметре или State. CompositionLocal је оптималан за конфигурационе податке који се ретко или никад не мењају.
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 // читање без експлицитног параметра
AsyncImage(model = user?.avatarUrl, contentDescription = "Avatar")
}
CompositionLocalProvider ствара област видљивости, унутар које LocalUser.current враћа постављену вредност. UserAvatar чита корисника без експлицитног преноса параметра кроз посредне функције. Ово је посебно вредно у дубоким хијерархијама, где су подаци потребни само у неколико листова чворова.
Често постављана питања
Промена State-а током Composition-а планира нову рекомпозицију која ће се извршити након завршетка тренутне. Не долази до петље: Compose гарантује да се свака рекомпозиција извршава у посебној трансакцији snapshot система.
На савременим уређајима Composition екрана са 50–100 Composable функција траје 1–5 ms. Google препоручује уклапање у 16 ms за кадар од 60fps. Ако Composition премашује ово ограничење, користите LazyColumn или разбијте екран на мање функције.
Директно ручно покретање Composition-а није могуће — њиме аутоматски управља Composer. Међутим, може се присилно заказати рекомпозиција променом State-а или позивањем invalidate() на коренском композиту, ако постоји приступ CompositionContext-у.
View хијерархија — непроменљиво стабло Java објеката које се креира једном. Composition — виртуелно стабло које се преграђује при свакој промени података. View чува своје стање у променљивама инстанце, Composition — у слотовима везаним за позицију позива функције.
Ако Composable функција престане да се позива (нпр. услов if постане false), Composition уклања њен чвор и позива чишћење DisposableEffect-а. При поновном појављивању (if поново true) креира се нови чвор — стари се не обнавља.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође