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

Есть ли лаги в Jetpack Compose в LazyList

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

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

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

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

Производительность Lazy-компонентов в Jetpack Compose

Однозначного ответа «да» или «нет» на вопрос о лагах в Jetpack Compose LazyList (LazyColumn/LazyRow) не существует. Их производительность зависит от множества факторов, и при правильном использовании они могут работать плавно (стабильные 60/120 FPS), но при ошибках в реализации — проявлять заметные фризы, дропы кадров (jank) и лаги.

В отличие от RecyclerView на View-системе, где лаги часто связаны с самим рендерингом сложных иерархий, в Compose основной источник проблем — чрезмерная рекомпозиция, дорогие вычисления в модели и неправильное управление состоянием.

Основные причины лагов в LazyList

  1. Чрезмерная или ненужная рекомпозиция:
    *   Если внутри `LazyColumn` элемент (item) рекомпозируется целиком при изменении состояния в другом элементе — это приводит к лагам. Ключ к решению — **использование `key` в `items()`** и стабильные, неизменяемые типы данных (`@Immutable`, `@Stable` аннотации).
    *   **Пример без ключа (плохо):**
    ```kotlin
    LazyColumn {
        items(users) { user ->
            UserItem(user) // При любом изменении списка Compose может перекомпоновывать все элементы
        }
    }
    ```
    *   **Пример с ключом (правильно):**
    ```kotlin
    LazyColumn {
        items(
            items = users,
            key = { user -> user.id } // Уникальный ключ позволяет Compose корректно идентифицировать и переиспользовать уже вычисленные состояния
        ) { user ->
            UserItem(user)
        }
    }
    ```

2. Тяжелые вычисления в модели Composable или внутри LazyColumn scope:

    *   Любые синхронные операции (парсинг JSON, чтение БД, сложные преобразования списков) **непосредственно в теле функции Composable** или лямбде `items()` блокируют поток UI.
    *   **Решение:** Выносить такие операции в `ViewModel` с использованием корутин (`viewModelScope.launch`), использовать кэширование и передавать в UI уже готовые, легковесные данные.

  1. Отсутствие derivedStateOf для преобразований, зависящих от состояния прокрутки:
    *   Если вы вычисляете что-то на основе `LazyListState` (например, отображаете кнопку "вверх", когда прокрутка больше 1000px), и это вычисление запускается при каждой рекомпозиции — это может тормозить.
    *   **Решение:**
    ```kotlin
    val listState = rememberLazyListState()
    val showButton by remember {
        derivedStateOf {
            listState.firstVisibleItemIndex > 0
        }
    }
    // `showButton` будет меняться только при реальном изменении условия, а не при каждой рекомпозиции
    ```

4. Использование некэшированных тяжелых ресурсов:

    *   Загрузка изображений без кэширования (`Coil`, `Glide` с компоуз-обвязками делают это автоматически) или чтение ресурсов каждый раз заново.
    *   **Решение:** Использовать `remember` для ресурсов, которые не должны пересоздаваться при рекомпозиции.
    ```kotlin
    val imagePainter = remember {
        // Инициализация, которая произойдет только один раз для данного места в композиции
        PainterResource(id = R.drawable.heavy_image)
    }
    ```

5. Слишком сложная или глубокая цепочка Modifier в каждом элементе списка. Особенно это касается модификаторов, требующих relayout (например, animateContentSize() в большом списке).

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

  • Layout Inspector и Compose Compiler Metrics: Позволяют увидеть, какие композаблы нестабильны (stable, unstable, skippable).
  • Профилировщик Android Studio (Compose): Вкладка Compose в профилировщике показывает количество рекомпозиций, пропусков, длительность каждой рекомпозиции.
  • JankStats библиотека: Для мониторинга дропов кадров в продакшене.

Практические рекомендации для плавного LazyList

  • Стабильные типы данных: Помечайте классы данных, передаваемые в списки, аннотациями @Immutable или @Stable.
  • Используйте items с key и по возможности contentType (для еще более эффективного переиспользования).
  • Выносите логику: В ViewModel. Используйте пагинацию (Paging 3.0 с компоуз-адаптером) для больших списков.
  • Оптимизируйте каждый item: Убедитесь, что он skippable (пропускаем при рекомпозиции) и не выполняет лишней работы.
  • remember для тяжелых объектов внутри item.
  • Избегайте вложенных ленивых списков (LazyColumn внутри LazyColumn), если это возможно. Если необходимо — используйте фиксированную высоту у внутреннего списка или subcompose (но это сложная тема).

Вывод: Сами по себе LazyColumn/LazyRow не являются источником лагов. Они — высокооптимизированные компоненты. Лаги возникают из-за некорректного управления состоянием и рекомпозицией внутри этих компонентов. Глубокая оптимизация требует понимания ментальной модели Compose: рекомпозиция — это не перерисовка, а перевычисление, и управлять ей нужно аккуратно, минимизируя объем работы.

Есть ли лаги в Jetpack Compose в LazyList | PrepBro