Styr på recomposition
Stabile parametre og færre recompositions.
Styr på recomposition er en gratis Android Academy-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Android Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Android Academy-kurset indeholder 4 lektioner i alt.
R rekomposition: Compose' performanceknap
I Jetpack Compose beskrives brugerfladen af funktioner. Når tilstanden ændres, udfører Compose rekomposition: De composables, der læser tilstanden, køres igen for at opdatere skærmen.
R rekomposition er normalt og billig, når den afgrænses rigtigt. Men når den sker for ofte eller over for meget af træet, bliver den den største årsag til hakken i Compose. I denne lektion lærer du at holde rekompositionen lille og sjælden.
Visning af antal rekompositioner
Mål, før du retter. Layout Inspector i Android Studio viser antallet af rekompositioner for hver composable i realtid. Du kan også bruge Compose-compilers målinger eller en hurtig fejlsøgnings-tæller.
Et enkelt trick: En SideEffect øger en reference, hver gang en composable rekomponeres, så du kan logge overraskelser under udviklingen.
@Composable
fun RecompositionCounter(tag: String) {
val count = remember { mutableStateOf(0) }
SideEffect { count.value++ }
Log.d("Recompose", "$tag recomposed ${count.value} times")
}
// Drop RecompositionCounter("PriceLabel") inside a composable
// to watch how often it re-runs while you interact.Læs tilstand så sent som muligt
Compose rekomponerer kun de composables, der læser en tilstandsværdi. Hvis en forælder læser tilstanden, rekomponeres hele forælderen; hvis kun et lille barn læser den, rekomponeres kun barnet.
Flyt derfor læsning af tilstand ned i træet. Her rekomponerer den dårlige version hele Column ved hvert tidsinterval, mens den gode version begrænser det til etiketten.
// BAD: Column reads `seconds`, so everything recomposes each second
@Composable
fun TimerBad(seconds: Int) {
Column {
ExpensiveHeader()
Text("Elapsed: $seconds")
}
}
// GOOD: only the Text reads the value via a lambda
@Composable
fun TimerGood(seconds: () -> Int) {
Column {
ExpensiveHeader()
Text("Elapsed: ${seconds()}")
}
}Udskyd læsninger med lambdafunktioner
Et stærkt mønster er at sende en værdi, der ændres ofte, som en lambda, der returnerer den. Den composable, der til sidst kalder lambdaen, er den eneste, der rekomponeres.
Det er derfor, Modifier.offset { ... } og graphicsLayer { ... } tager lambdafunktioner: Rulle- og animationsværdier ændres ved hvert billede, og lambdaen holder rekomposition ude af layoutfasen.
// Passing the value: parent recomposes every frame of scroll
Box(Modifier.offset(y = scrollOffset.dp))
// Passing a lambda: skips recomposition, updates in the layout phase
Box(Modifier.offset { IntOffset(x = 0, y = scrollOffset.roundToInt()) })
// Same idea for alpha/scale during animation:
Image(
painter = painter,
contentDescription = null,
modifier = Modifier.graphicsLayer { alpha = animatedAlpha() }
)Stabilitet: Hvorfor Compose springer over
Compose kan springe over rekomposition af en composable, hvis alle dens parametre er stabile og uændrede. En type er stabil, når Compose kan stole på, at equals afspejler reelle ændringer, og at dens offentlige felter ikke ændres ubemærket.
- Stabile: primitive typer,
String, uforanderlige dataklasser,State. - Ustabile:
List/Map-grænseflader, klasser medvar-felter, typer fra moduler uden compileren.
En ustabil parameter tvinger rekomposition frem, selv når intet er ændret.
// UNSTABLE: List is an interface; Compose cannot assume immutability,
// so UserList recomposes even if the contents are identical.
@Composable
fun UserList(users: List<User>) { /* ... */ }
data class User(val id: Long, val name: String) // stable: all valsGør parametre stabile
To almindelige løsninger på ustabile parametre:
- Brug uforanderlige samlinger fra
kotlinx.collections.immutable(f.eks.ImmutableList), som Compose behandler som stabile. - Annotér en klasse, du selv kontrollerer, med
@Immutableeller@Stablefor at love Compose, at den ikke ændrer sig.
Nu kan Compose trygt springe rekomposition over, når den samme instans sendes igen.
import kotlinx.collections.immutable.ImmutableList
import androidx.compose.runtime.Immutable
@Immutable
data class UiState(
val title: String,
val users: ImmutableList<User>
)
// Stable parameter -> Compose can skip this when state is unchanged
@Composable
fun UserList(users: ImmutableList<User>) { /* ... */ }remember: Beregn ikke igen ved hvert billede
Composables kan køre mange gange. Enhver beregning, der ikke er helt enkel, og som udføres direkte i kroppen, kører ved hver rekomposition. Pak den ind i remember, så den kun beregnes igen, når dens nøgler ændres.
For værdier, der er afledt af anden tilstand, bør du foretrække derivedStateOf, som kun udsender igen, når det beregnede resultat rent faktisk ændrer sig.
// Recomputes the sorted list only when `items` changes
val sorted = remember(items) { items.sortedBy { it.name } }
// derivedStateOf: only triggers readers when the BOOLEAN flips,
// not on every scroll pixel
val showButton by remember {
derivedStateOf { listState.firstVisibleItemIndex > 5 }
}
if (showButton) ScrollToTopButton()Stabile nøgler i lazy-lister
I LazyColumn/LazyRow skal du give hvert element en stabil nøgle. Uden nøgler tvinger indsættelse eller omorganisering af elementer Compose til at rekomponere og måle elementer igen, som reelt ikke er ændret, fordi de spores efter placering.
Med en stabil nøgle kan Compose matche elementer på tværs af opdateringer og springe de uændrede over.
LazyColumn {
items(
items = users,
key = { user -> user.id } // stable identity
) { user ->
UserRow(user)
}
}
// Now adding a user at the top reuses existing rows
// instead of recomposing the whole list.Løft tilstand op, og send hændelser op
Ustabile lambda-parametre kan også forhindre, at Compose springer over, hvis der oprettes en ny lambda-instans ved hver rekomposition. Gør tilbagekald stabile ved at huske dem eller referere til stabile funktioner.
Kombineret med tilstandsløftning (tilstanden bor hos kalderen, og hændelser bevæger sig opad) holder dette leaf-composables billige og mulige at springe over.
@Composable
fun SearchBar(query: String, onQueryChange: (String) -> Unit) {
TextField(value = query, onValueChange = onQueryChange)
}
// In the caller, a remembered lambda keeps the reference stable:
val onChange = remember { { newValue: String -> viewModel.setQuery(newValue) } }
SearchBar(query = query, onQueryChange = onChange)Undgå at læse rulle-/animationstilstand for højt i træet
En klassisk fejl er at læse en tilstand, der ændrer sig hurtigt (rulleforskydning, animationsforløb), i en composable på højt niveau. Det tvinger hele undertræet til at rekomponere ved hvert billede.
Hold sådanne læsninger inde i Modifier-lambdaer (offset {}, graphicsLayer {}, drawBehind {}), så arbejdet sker i layout- eller tegnefasen og ikke under rekomposition. Det er forskellen på jævn rulning ved 60 billeder i sekundet og rulning med hakken.
// Animated color used only for drawing -> stay in the draw phase
Box(
Modifier.drawBehind {
drawRect(color = animatedColor()) // lambda read, no recomposition
}
)En tjekliste til rekomposition
Når en skærm føles hakkende under interaktion, skal du gennemgå denne liste:
- Er parametrene stabile (uforanderlige data,
ImmutableList)? - Læser du tilstand, der ændrer sig hurtigt, så langt nede som muligt, helst i modifier-lambdaer?
- Har lazy-lister stabile nøgler?
- Er dyre beregninger placeret bag
remember/derivedStateOf? - Bekræftede Layout Inspector, at antallet rent faktisk faldt?
Hvert felt, du markerer, fjerner spildt arbejde fra hvert billede.
Hurtigt tjek
Du sender en List<User> til en composable og bemærker, at den rekomponeres, selv når dataene er uændrede. Hvad er den mest direkte løsning?
Opsummering: Mindre og sjældnere rekomposition
Du har lært at tæmme rekomposition, som er det største performanceproblem i Compose:
- Mål antallet med Layout Inspector eller en fejlsøgnings-tæller.
- Læs tilstand så langt nede som muligt; udskyd læsninger med lambdafunktioner.
- Gør parametre stabile med uforanderlige data og
@Immutable/ImmutableList. - Brug
rememberogderivedStateOffor at undgå genberegning ved hvert billede. - Giv lazy-elementer stabile nøgler; hold læsninger af hurtigt skiftende tilstand i modifier-lambdaer.
Nu går vi fra CPU/rekomposition til hukommelse: at finde og rette lækager.
Lær Kotlin med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 36
- Lektioner
- 152
Ofte stillede spørgsmål
Er lektionen “Styr på recomposition” gratis?
Ja — alle 3 lektioner i læringssporet Android Academy, inklusive “Styr på recomposition”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Android Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Styr på recomposition”?
Stabile parametre og færre recompositions. Du øver dig i Android Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Android Academy?
Der kræves ingen tidligere erfaring. Android Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.
Hvor lang tid tager lektionen “Styr på recomposition”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Android Academy-lektion?
Ja. Alle Android Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Måling af ydeevne
- Styr på recomposition
- Memory leaks og løsninger
- Opstart og baseline-profiler