Как избежать большого количества рекомпозиций?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Оптимизация рекомпозиций в Jetpack Compose: стратегии и лучшие практики
Основная цель оптимизации — минимизировать количество вызовов recompose scope, особенно для дорогих в вычислениях UI-элементов. Вот ключевые стратегии:
1. Правильное использование ключевых модификаторов и функций
Стабильные классы данных — помечайте data классы как @Stable или используйте @Immutable для коллекций, чтобы Compose мог лучше отслеживать изменения:
@Immutable
data class UserState(val name: String, val score: Int)
Ключи для Lazy-списков — всегда предоставляйте стабильные ключи:
LazyColumn {
items(
items = users,
key = { it.id } // Уникальный стабильный идентификатор
) { user ->
UserItem(user)
}
}
2. Контроль области рекомпозиции
Дробление на мелкие composable-функции — Compose рекомпозирует только измененные функции:
// ПЛОХО: весь блок будет рекомпозирован при изменении count
@Composable
fun BadCounter() {
Column {
Text("Заголовок") // Рекомпозируется зря
Text("Счет: $count")
}
}
// ХОРОШО: только нужная часть
@Composable
fun GoodCounter() {
Column {
Header() // Стабильный элемент
CounterText(count) // Рекомпозируется только при изменении count
}
}
@Composable
fun CounterText(count: Int) {
Text("Счет: $count")
}
3. Оптимизация с помощью производных состояний
remember и derivedStateOf — предотвращают рекомпозиции при незначительных изменениях:
@Composable
fun ExpensiveList(items: List<Item>) {
val visibleItems = remember(items) {
derivedStateOf {
items.filter { it.isVisible }
}
}
// Рекомпозиция только при реальном изменении visibleItems
LazyColumn {
items(visibleItems.value) { item ->
ItemRow(item)
}
}
}
4. Использование side effects с умом
LaunchedEffect и DisposableEffect — выносите логику, не связанную с UI:
@Composable
fun TimerDisplay() {
var time by remember { mutableStateOf(0) }
LaunchedEffect(Unit) { // Запускается один раз
while (true) {
delay(1000)
time++ // Изменение состояния вызывает рекомпозицию только Text
}
}
Text("Прошло секунд: $time") // Рекомпозируется только этот элемент
}
5. Оптимизация работы с состояниями
Локальное состояние vs ViewModel — храните UI-состояние локально, если оно не нужно бизнес-логике:
@Composable
fun ToggleButton() {
var isChecked by remember { mutableStateOf(false) } // Локальное состояние
Switch(
checked = isChecked,
onCheckedChange = { isChecked = it } // Рекомпозируется только Switch
)
}
State hoisting — поднимайте состояние к месту, где оно реально используется:
@Composable
fun ParentComponent() {
var textState by remember { mutableStateOf("") }
// Дочерние компоненты получают только необходимые параметры
ChildInput(text = textState, onTextChange = { textState = it })
}
@Composable
fun ChildInput(text: String, onTextChange: (String) -> Unit) {
TextField(value = text, onValueChange = onTextChange)
}
6. Инструменты отладки и анализа
Включите в build.gradle:
composeOptions {
kotlinCompilerExtensionVersion = "1.5.4"
}
Используйте модификатор debugInspectorInfo или Layout Inspector в Android Studio для визуализации рекомпозиций.
7. Дополнительные техники
rememberс вычислениями — кэшируйте тяжелые вычисления- Пропускайте параметры по умолчанию — Compose пропускает рекомпозицию, если все параметры имеют стабильные значения и не изменились
- Используйте
@ReadOnlyComposableдля функций, которые только читают данные
Ключевой принцип: рекомпозиция — это не всегда плохо. Jetpack Compose оптимизирован для быстрых рекомпозиций. Проблемой становится только избыточная рекомпозиция дорогих вычислений. Измеряйте производительность с помощью Benchmark и JankStats, прежде чем оптимизировать. Часто достаточно разделить крупные composable-функции на более мелкие, чтобы добиться значительного улучшения производительности.