Зависание списка на Android обычно означает, что главный поток занят сетью, базой, преобразованием данных, декодированием изображений или слишком тяжелой отрисовкой. Ускорение одного адаптера не поможет, если приложение получает и обрабатывает тысячи элементов до первого кадра.

Снимите trace проблемного запуска и разделите время на сеть, parsing, запрос БД, mapping и UI. Любая тяжелая операция должна уйти с main thread, а список — загружаться страницами и переиспользовать элементы.

Что проверить в первую очередь

Начните с воспроизводимого сценария: зафиксируйте время сбоя, идентификатор объекта, версию приложения или конфигурации и последнее известное рабочее состояние. Не меняйте несколько параметров одновременно. Один контролируемый шаг должен подтверждать или исключать одну гипотезу, иначе временное исчезновение симптома легко принять за исправление. Перед работой с данными и настройками подготовьте резервную копию и понятный способ отката.

  • Проверьте Logcat на ANR, skipped frames и NetworkOnMainThreadException.
  • Измерьте время до первого элемента и до полной загрузки.
  • Уточните размер ответа, число объектов и объем изображений.
  • Сравните поведение release-сборки на слабом физическом устройстве.

Почему возникает проблема

Внешний симптом часто появляется не в том компоненте, где возникла первичная ошибка. Интерфейс может показывать неверное состояние из-за backend, очереди, кеша, прав доступа или внешнего API. Полезно проследить данные от источника до результата и найти первую точку расхождения. Это надежнее, чем исправлять последнее сообщение об ошибке или бесконечно перезапускать сервис.

  • JSON разбирается или сортируется в главном потоке.
  • RecyclerView или Compose получает новый полный список при каждом изменении.
  • Изображения декодируются в исходном разрешении без кеша.
  • Запрос Room не индексирован и возвращает лишние столбцы.
  • Пагинация запрашивает следующие страницы циклически из-за неверного ключа.

Пошаговая диагностика

Диагностику проводите на тестовой записи или отдельном окружении. В журналах скрывайте токены, пароли, персональные данные и содержимое документов. Для каждого шага сохраняйте измеримый результат: код ответа, версию записи, идентификатор события, состояние процесса, контрольную сумму или время выполнения. Сравнение одной и той же операции до и после изменения помогает отделить причину от совпадения.

  • Запишите System Trace или Perfetto и найдите длинные участки main thread.
  • Добавьте отдельные тайминги сети, parsing, БД и bind/composition.
  • Проверьте количество recomposition или bind для одного экрана.
  • Временно замените изображения заглушками, чтобы оценить их вклад.
  • Проверьте SQL query plan и размер локальной таблицы.

Как разделить загрузку данных и отрисовку

Пользователю важен быстрый первый полезный кадр, а не мгновенная обработка всего набора. Поток данных должен отдавать небольшие страницы, а интерфейс — обновлять только изменившиеся элементы.

  • Сеть возвращает страницу с устойчивым cursor или page key.
  • Repository преобразует данные на background dispatcher.
  • База хранит кеш и отдает наблюдаемый поток ограниченного размера.
  • UI использует стабильные keys и diff вместо полной перерисовки.
  • Изображения загружаются по размеру контейнера с memory/disk cache.

Как исправить проблему

Разбейте исправление на небольшие обратимые изменения. Сначала устраните подтвержденную причину, затем повторите исходный сценарий и проверьте соседние функции. Массовое обновление данных запускайте на ограниченной выборке с отчетом и только после сверки расширяйте на весь объем. Не отключайте авторизацию, валидацию, шифрование или проверку сертификатов ради быстрого исчезновения ошибки.

  • Перенесите parsing, сортировку и запросы БД в Dispatchers.IO/Default по назначению.
  • Внедрите Paging 3 или контролируемую cursor pagination.
  • Для RecyclerView используйте ListAdapter/DiffUtil; для Compose — immutable state и стабильные key.
  • Уменьшайте изображения на сервере или через image loader до размера экрана.
  • Добавьте индексы БД и не выбирайте поля, которые не нужны карточке.

Безопасный порядок внедрения

  • Сохраните затрагиваемые данные, конфигурацию и текущие журналы, заранее проверив реальный способ восстановления.
  • Повторите проблему на тестовом объекте без реальных списаний, рассылок и необратимых изменений клиентских данных.
  • Зафиксируйте изменение в системе контроля версий или журнале работ вместе с причиной и планом отката.
  • Проведите тест на нормальном сценарии, ошибочном вводе, повторном запросе, параллельной операции и временной недоступности зависимости.
  • После выпуска наблюдайте логи, метрики и полный пользовательский путь, а не только один успешный запрос.

Как проверить результат

Разовый успешный тест недостаточен. Повторите операцию, проверьте крайние значения, одновременные действия и восстановление после перезапуска или временного сбоя. Для важного сценария сохраните автоматический тест либо короткий регрессионный чек-лист. Итог должен подтверждаться не только интерфейсом, но и состоянием базы, очереди, внешнего сервиса и журналом действий.

  • Главный поток не содержит длинных блокирующих операций при открытии экрана.
  • Первый экран появляется за приемлемое время на слабом устройстве и медленной сети.
  • Прокрутка не запускает дублирующиеся запросы и не пропускает элементы.
  • Поворот экрана и возврат из фона не загружают весь список заново.

Типичные ошибки при исправлении

  • Отключать StrictMode и считать предупреждения несущественными.
  • Загружать все страницы заранее ради простой логики.
  • Вызывать notifyDataSetChanged для каждого небольшого изменения.
  • Оптимизировать только эмулятор или debug-сборку.

Как предотвратить повторение

Профилактика строится вокруг явных контрактов, повторяемых релизов и наблюдаемости. Система должна не только работать сейчас, но и позволять быстро увидеть нарушение правила при следующем обновлении, росте нагрузки или сбое внешнего сервиса. Проверки полезно автоматизировать там, где ошибка уже привела к потерям времени, данных или заявок.

  • Добавьте performance-тест открытия списка на реалистичном объеме.
  • Следите за ANR, slow frames и размером сетевых ответов в production.
  • Ограничьте максимальный размер страницы и изображений контрактом API.
  • Проверяйте Compose recomposition или RecyclerView bind после изменений карточки.

Что подготовить для технического разбора

  • Описание ожидаемого и фактического поведения с точной последовательностью действий.
  • Время проблемы, идентификатор тестового объекта и версии затронутых компонентов.
  • Фрагменты журналов до и после ошибки без секретов и персональных данных.
  • Перечень последних изменений и уже выполненных проверок.
  • Безопасный доступ к тестовой среде либо способ воспроизвести сбой без влияния на клиентов.

Частые вопросы

Почему список плавный на новом телефоне, но зависает у клиентов?

Мощный процессор и быстрый диск скрывают блокировки. Проверяйте слабое устройство, release-сборку и большой набор данных.

Нужно ли сразу переходить с RecyclerView на Compose?

Нет. Оба подхода могут работать быстро; сначала найдите блокировку, лишние обновления и объем данных.

Когда нужна помощь специалиста

Если Android-приложение зависает на списках, я могу снять trace, найти нагрузку main thread, оптимизировать сеть, БД, пагинацию и отрисовку, а затем проверить результат на реальном устройстве.