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

Как Jetpack Compose понимает что нужно выполнить рекомпозицию

2.0 Middle🔥 151 комментариев
#UI и вёрстка#Архитектура и паттерны

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

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

Механизм Recomposition в Jetpack Compose

Рекомпозиция - это процесс перерисовки UI когда изменилось состояние. Это ключ к пониманию Compose.

Основной механизм: State Tracking

Compose отслеживает состояние и его изменения:

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }  // Состояние
    
    Column {
        Text("Count: $count")  // Зависит от count
        
        Button(onClick = { count++ }) {  // Изменит count
            Text("Increment")
        }
    }
}

// Когда count изменилась, Compose автоматически вызывает рекомпозицию

Как Compose отслеживает изменения

1. mutableStateOf + remember

var state by remember { mutableStateOf(initialValue) }

// Эквивалент (без remember):
var state by mutableStateOf(initialValue)  // Потеряется при рекомпозиции!

// remember гарантирует сохранение состояния

Внутренний механизм:

  1. mutableStateOf создает State объект
  2. remember кеширует этот объект
  3. При изменении State, подписчики уведомляются
  4. Compose перезапускает composable функцию

State объект и подписка

// Под капотом
class MutableState<T>(private var _value: T) : State<T> {
    private val subscribers = mutableSetOf<(T) -> Unit>()
    
    override var value: T
        get() = _value
        set(newValue) {
            if (_value != newValue) {
                _value = newValue
                subscribers.forEach { it(newValue) }  // Уведомить
            }
        }
}

// Compose подписывается при использовании в composable
@Composable
fun MyComposable(state: State<String>) {
    val value = state.value  // Compose отмечает зависимость
    Text(value)  // Зависит от value
}

Snapshot System: Отслеживание изменений

Compose использует Snapshot для отслеживания:

// Для каждой рекомпозиции создается snapshot
val currentSnapshot = Snapshot.current()

var name by remember { mutableStateOf("John") }
var age by remember { mutableStateOf(30) }

// Snapshot отслеживает, какие State были прочитаны
// Если прочитанный State изменился → рекомпозиция

Когда происходит рекомпозиция

1. Изменение State, который используется в composable

@Composable
fun MyScreen() {
    var counter by remember { mutableStateOf(0) }
    
    // Эта ветка использует counter
    Text("Counter: $counter")  // Зависит от counter
    
    Button(onClick = { counter++ }) {
        Text("Increment")
    }
    // counter++ → Text перерисуется
}

2. Изменение параметра composable

@Composable
fun Parent() {
    var name by remember { mutableStateOf("John") }
    
    Child(name)  // Передали name
    
    Button(onClick = { name = "Jane" }) {
        Text("Change")
    }
}

@Composable
fun Child(name: String) {
    Text(name)  // Зависит от параметра
}
// name изменилась → Child перерисуется

Что НЕ вызывает рекомпозицию

@Composable
fun Example() {
    // ПЛОХО: просто переменная, не State
    var counter = 0
    
    Button(onClick = { counter++ }) {  // Увеличит локальную переменную
        Text("Counter: $counter")       // Но не вызовет рекомпозицию!
    }
    // Текст всегда показывает 0!
}

// ПРАВИЛЬНО:
@Composable
fun Example() {
    var counter by remember { mutableStateOf(0) }
    
    Button(onClick = { counter++ }) {  // Изменит State
        Text("Counter: $counter")       // Вызовет рекомпозицию
    }
}

Ветвление и рекомпозиция

@Composable
fun Greeting(name: String) {
    var showDetails by remember { mutableStateOf(false) }
    
    Text("Hello $name")
    
    if (showDetails) {  // Условное выполнение
        Text("Details...")
    }
    
    Button(onClick = { showDetails = !showDetails }) {
        Text("Toggle")
    }
}

// showDetails = false → "Details" не в дереве
// showDetails = true → "Details" появляется
// Compose добавляет/удаляет элементы из дерева

Оптимизация: derivedStateOf

@Composable
fun SearchResults(query: String, results: List<String>) {
    var selectedIndex by remember { mutableStateOf(0) }
    
    // ПЛОХО: пересчитывается при каждой рекомпозиции
    val filteredResults = results.filter { it.contains(query) }
    
    // ХОРОШО: пересчитывается только если query или results изменились
    val filteredResults by remember(query, results) {
        derivedStateOf { results.filter { it.contains(query) } }
    }
    
    LazyColumn {
        items(filteredResults) { result ->
            Text(result)
        }
    }
}

skipDefaultParamComparison

@Composable
fun User(user: User) {
    Text(user.name)  // Если user остается тем же объектом → no recomposition
}

// ПРОБЛЕМА: новый объект User каждый раз
@Composable
fun Parent() {
    val user = User(name = "John")  // Новый объект каждый раз!
    User(user)  // Всегда рекомпозиция
}

// РЕШЕНИЕ: мемоизация
@Composable
fun Parent() {
    val user = remember { User(name = "John") }  // Один объект
    User(user)  // Рекомпозиция только если user.name изменилась
}

Scope рекомпозиции

@Composable
fun Parent() {
    var parentCounter by remember { mutableStateOf(0) }
    var childCounter by remember { mutableStateOf(0) }
    
    Text("Parent: $parentCounter")  // Зависит от parentCounter
    
    Child(childCounter) { newValue ->
        childCounter = newValue
    }
    
    Button(onClick = { parentCounter++ }) {}
}

@Composable
fun Child(counter: Int, onUpdate: (Int) -> Unit) {
    Text("Child: $counter")
    Button(onClick = { onUpdate(counter + 1) }) {}
}

// parentCounter++ → Parent перерисуется
// → Child получит новое значение
// → Child перерисуется

Инструменты отладки

LayoutInspector - показывает дерево composable
Compose Metrics - анализирует рекомпозиции
RecomposeHighlighter - подсвечивает перерисовки

Лучшие практики

✅ Делай так:

  • Используй remember для сохранения состояния
  • Используй remember(key) для зависимостей
  • Используй derivedStateOf для вычислений
  • Мемоизируй вычисляемые значения
  • Разделяй крупные composable на меньше

❌ Избегай:

  • Создания новых объектов без remember
  • Простых переменных вместо State
  • Ненужных рекомпозиций
  • Сложной логики в composable

Вывод

Compose отслеживает рекомпозицию через:

  1. State Tracking - отслеживание изменений mutableStateOf
  2. Snapshot System - регистрация прочитанных State
  3. Dependency Graph - какой composable зависит от какого State
  4. Scope - перезапуск только затронутых composable

Этот механизм позволяет эффективно обновлять UI при изменении состояния.