{}const=>[]async()letfn</>var
Mobile Разработка

Почему мобильные приложения лагают: разбор производительности без магии

Почему даже современные мобильные приложения могут тормозить, фризить и жрать ресурсы? Разбираем UI thread, память, сеть, анимации и типичные ошибки разработчиков простым и живым языком.

К

Кодик

Автор

5 мин чтения

Ты открываешь приложение. Жмёшь кнопку. И… интерфейс замирает, скролл дёргается, экран думает дольше, чем junior перед вопросом «а что такое замыкание?».

Пользователь в этот момент не думает про архитектуру, UI thread, память и сетевые запросы. Он думает проще:

«Приложение лагает. Удаляю».

Что вообще значит «приложение лагает»?

Лаг — это момент, когда приложение не успевает быстро отреагировать на действие пользователя. Нажал кнопку — задержка. Листнул экран — рывок. Открыл раздел — вечный загрузчик.

В идеальном мире интерфейс должен работать плавно. Обычно ориентир — 60 кадров в секунду. Это значит, что у приложения есть примерно 16 миллисекунд на подготовку одного кадра.

Если приложение не укладывается в это время, кадр пропускается. Пользователь видит фриз, дёргание или микрозависание.

А если экран 120 Гц, времени ещё меньше. Там приложение должно быть не просто быстрым, а почти ниндзя.

🔥 100 000+ учеников уже с нами

Устал читать теорию?
Пора кодить!

Кодик — приложение, где ты учишься программировать через практику. AI-наставник, интерактивные уроки, реальные проекты.

🤖 AI 24/7
🎓 Сертификаты
💰 Бесплатно
🚀 Начать учиться
Присоединились сегодня

Главный подозреваемый — главный поток

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

И если загрузить этот поток тяжёлой работой, приложение начинает вести себя как кассир в супермаркете, которому одновременно дали пробить товары, оформить возврат, вызвать охрану и объяснить бабушке, где хлеб.

Например, плохая идея — делать тяжёлые вычисления прямо там, где должен жить интерфейс:

// Плохая идея
function onButtonClick() {
  const result = veryHeavyCalculation();
  updateScreen(result);
}

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

Слишком много перерисовок

Ещё одна классика — приложение слишком часто обновляет интерфейс. Особенно это больно в React Native, Flutter и других UI-фреймворках.

Разработчик меняет состояние на каждый чих, компонент перерисовывается, за ним ещё один, потом список, потом карточки, потом весь экран. И вот уже обычный скролл превращается в презентацию PowerPoint.

// Условный пример боли
setState(value);
setState(loading);
setState(user);
setState(items);
setState(filters);

Сам по себе setState не зло. Зло — когда приложение обновляет половину интерфейса там, где можно было аккуратно обновить один маленький кусочек.

Тяжёлые списки: кладбище FPS

Ленты, каталоги, чаты, таблицы, списки товаров — всё это может легко убить производительность.

Особенно если на экране сразу рендерится слишком много элементов. Например, 1000 карточек, каждая с картинкой, кнопками, тенями, анимациями и логикой.

Телефон в этот момент такой:

«Я мобильное устройство, а не дата-центр».

Для списков нужно использовать виртуализацию, ленивую загрузку, пагинацию и нормальную работу с изображениями. Не надо грузить весь склад товаров на первый экран.

Картинки на 18 мегабайт

Отдельная боль — изображения. Очень часто приложение тормозит не из-за сложной бизнес-логики, а потому что кто-то решил показать маленькую аватарку через PNG размером с дипломную работу.

Большие картинки:

  • долго загружаются;

  • занимают много памяти;

  • тормозят скролл;

  • могут вызывать подвисания при декодировании;

  • быстро превращают приложение в обогреватель.

Изображения нужно сжимать, отдавать в подходящем размере, использовать современные форматы и не грузить оригинал туда, где нужна миниатюра.

Сеть: когда приложение ждёт интернет как второе пришествие

Мобильное приложение живёт не в идеальном мире. Пользователь может быть в метро, лифте, деревне, торговом центре или просто рядом с роутером, который работает на силе надежды.

Если приложение бездумно делает запросы на каждый экран, каждый свайп и каждое открытие, оно начинает лагать не только из-за кода, но и из-за ожидания сети.

// Плохой подход
loadUser();
loadProfile();
loadSettings();
loadOrders();
loadRecommendations();
loadSomethingElseBecauseWhyNot();

Нормальное приложение должно кэшировать данные, показывать skeleton-экраны, не блокировать интерфейс и спокойно переживать плохой интернет.

Memory leak: приложение забывает забывать

Memory leak — это когда приложение создаёт объекты, подписки, таймеры или ссылки, но потом не освобождает их.

Сначала всё работает нормально. Потом приложение открывают 10 минут, переходят между экранами, возвращаются назад, снова открывают разделы — и память начинает расти.

В какой-то момент сборщик мусора начинает работать чаще, интерфейс дёргается, телефон греется, а пользователь чувствует, что приложение «устало жить».

Типичный пример:

// Подписались
subscribeToUpdates();

// А отписаться забыли
// classic...

Поэтому важно чистить подписки, таймеры, listeners и всё, что продолжает жить после закрытия экрана.

Анимации: красиво, пока не больно

Анимации делают приложение живым. Но если переборщить, они делают его мёртвым.

Blur, тени, прозрачность, параллакс, сложные переходы, видеофоны, glassmorphism — всё это может выглядеть дорого на макете и очень грустно на слабом Android.

Хорошая анимация должна помогать интерфейсу, а не устраивать бенчмарк GPU на каждом открытии экрана.

«У меня не лагает» — не аргумент

Главная ловушка разработчика — тестировать приложение только на своём устройстве.

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

У пользователя может быть:

  • старый Android;

  • мало оперативной памяти;

  • плохой интернет;

  • 20 приложений в фоне;

  • почти разряженная батарея;

  • экран с высокой частотой обновления;

  • и большое желание удалить всё, что бесит.

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

Как искать проблемы с производительностью

Самый плохой способ — смотреть на приложение и говорить: «ну вроде норм».

Нормальный способ — использовать инструменты:

  • профайлеры;

  • FPS monitor;

  • memory inspector;

  • network inspector;

  • flame graph;

  • логи долгих операций;

  • тесты на слабых устройствах.

Производительность — это не ощущение. Это измерения.

Что реально помогает ускорить приложение

Вот базовый набор вещей, которые часто дают заметный эффект:

  • не выполнять тяжёлую работу в UI thread;

  • уменьшать количество лишних перерисовок;

  • использовать виртуализацию списков;

  • кэшировать данные;

  • оптимизировать изображения;

  • не делать лишние сетевые запросы;

  • чистить подписки и таймеры;

  • упрощать тяжёлые анимации;

  • проверять приложение на слабых устройствах;

  • измерять производительность инструментами, а не глазами.

Кстати, про обучение разработке

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

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

А ещё у Кодика есть Telegram-сообщество для разработчиков. Там выходят полезные посты про программирование, backend, frontend, мобильную разработку, AI, DevOps, алгоритмы и всякую боль, которую понимает каждый, кто хоть раз чинил баг в пятницу вечером.

🎯Хватит откладывать

Понравилась статья?
Пора применять на практике!

В Кодик ты не просто читаешь — ты сразу пишешь код. Теория + практика = реальный скилл.

Мгновенная практика
🧠AI объяснит код
🏆Сертификат

Без регистрации • Без карты