← Назад к вопросам

Что такое Recomposition?

2.0 Middle🔥 291 комментариев
#UI и вёрстка#Производительность и оптимизация

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Recomposition (Рекомпозиция) в Jetpack Compose?

Рекомпозиция — это фундаментальный процесс в Jetpack Compose, фреймворке для построения UI в Android. Это механизм, при котором Compose автоматически заново вызывает те @Composable-функции, данные которых изменились, чтобы перерисовать (или "пересоставить") только обновленные части пользовательского интерфейса. Вместо того чтобы вручную обновлять иерархию виджетов, как в традиционном View-системе, Compose сам интеллектуально определяет, что нуждается в перерисовке, когда меняется состояние (State).

Простыми словами, рекомпозиция — это "реактивный перерасчет UI" в ответ на изменение состояния. Если упростить аналогией: ваше приложение — это функция, которая принимает состояние (State) и возвращает UI (UI = f(State)). При изменении состояния State функция f вызывается снова, и UI пересчитывается — это и есть рекомпозиция.

Ключевые принципы и особенности

  1. Реактивность на основе State: Рекомпозиция инициируется изменением объектов состояния, таких как MutableState<T>, StateFlow, LiveData (при использовании collectAsStateWithLifecycle) и других. Когда значение состояния обновляется, все @Composable-функции, которые его читают (read), помечаются для повторного выполнения.

    @Composable
    fun Counter() {
        // count — это состояние. Изменение count вызовет рекомпозицию этой функции.
        val count = remember { mutableStateOf(0) }
    
        Button(onClick = { count.value++ }) { // Клик меняет состояние -> рекомпозиция
            Text(text = "Clicked ${count.value} times")
        }
    }
    
  2. Инкрементальность и оптимизация: Compose не перерисовывает весь экран с нуля. Он запускает рекомпозицию только для тех конкретных @Composable-функций, чьи входные параметры изменились. Механизм Positional Memoization (запоминание позиции в дереве) и система скиаграмм (скрытых узлов-меток) позволяют Compose интеллектуально сравнивать предыдущее и новое дерево композиции, повторно используя уже вычисленные части.

  3. Идемпотентность и отсутствие побочных эффектов: @Composable-функции могут вызываться много раз (в т.ч. при рекомпозиции) и в любом порядке. Они должны быть идемпотентными — при одинаковых входных параметрах возвращать один и тот же UI. Побочные эффекты (запросы к сети, подписки на базу данных) внутри них недопустимы. Для эффектов существуют специальные Effect API, такие как LaunchedEffect, SideEffect, DisposableEffect.

  4. "Умное" пропускание (Skipping): Compose может и пропустить рекомпозицию функции, если понимает, что все её параметры стабильны (stable) и не изменились. Стабильность типа определяется аннотацией @Stable или выполнением условий: equals всегда возвращает один и тот же результат для двух одинаковых экземпляров, и уведомление об изменении публикуется, когда все открытые свойства изменяются.

    // Параметр `message` стабилен (String). Если при рекомпозиции родителя
    // `message` остался тем же, эта функция будет пропущена.
    @Composable
    fun Greeting(message: String) {
        Text(text = message)
    }
    

Жизненный цикл и этапы рекомпозиции

  1. Изменение состояния (State Change): Источник рекомпозиции.
  2. Планирование рекомпозиции (Schedule Recompose): Compose определяет, какие области дерева (RecomposeScope) зависят от изменившегося состояния.
  3. Выполнение рекомпозиции (Execute Recompose): Помеченные @Composable-функции вызываются заново.
  4. Генерация дерева композиции (Generate Composition): Создается обновленное дерево, описывающее UI.
  5. Отрисовка кадра (Frame Drawing): Дерево передается на этапы Layout (раскладка) и Drawing (рисование).

Важные практики для управления рекомпозицией

  • Использование remember: Кэширует результат вычисления или объект состояния на протяжении рекомпозиций, чтобы избежать дорогостоящих повторных вычислений или потери состояния.
    val expensiveResult = remember(key1 = someKey) {
        performHeavyCalculation()
    }
    
  • Разделение на мелкие композабли: Чем меньше функция, тем выше вероятность, что Compose сможет её пропустить при рекомпозиции.
  • Использование производных состояний (derivedStateOf): Позволяет создать состояние, которое вычисляется из других, предотвращая рекомпозицию при каждом изменении исходных состояний, а только когда меняется итоговый результат.
  • Структурирование с key и `Lazy-колонками/рядами:** Помогает Compose корректно идентифицировать и повторно использовать элементы в динамических списках.
    LazyColumn {
        items(users, key = { it.id }) { user -> // `key` помогает при перемещениях/обновлениях
            UserRow(user)
        }
    }
    

Итог: Рекомпозиция — это "движок" реактивного UI в Compose. Понимание её принципов (идемпотентность, стабильность, управление состоянием) критически важно для создания производительных и отзывчивых интерфейсов, так как позволяет избежать лишних перерисовок и эффективно обновлять только необходимые элементы экрана.