Производительность Flutter-приложения: где теряются кадры | FITTIN
+7 (800) 444-11-27
Позвоните — обсудим ваш проект
Сергей CCO FITTIN
Сергей CCO FITTIN
Напишите мне в Telegram
Обсудить проект
Производительность Flutter-приложения: где теряются кадры и как ускорить, обложка статьи FITTIN

Производительность Flutter-приложения: где теряются кадры и как ускорить

Плавность приложения пользователь считывает раньше, чем набор функций: экран либо листается ровно, либо дёргается. За этим ощущением стоит понятная механика — устройство успевает или не успевает нарисовать очередной кадр за отведённое ему время. Разница между приложениями лежит не в языке разработки и не в модели телефона, а в том, что именно происходит внутри этого промежутка. Дальше — как Flutter собирает кадр, где чаще всего теряется плавность, чем её измеряют и в каком порядке исправляют.


Что такое производительность приложения: кадр и бюджет времени

Экран телефона обновляет картинку с постоянной частотой: 60 раз в секунду на большинстве устройств, 90 или 120 — на аппаратах с быстрым экраном. Каждое такое обновление — кадр. На один кадр при 60 Гц приходится около 16,7 миллисекунды, при 120 Гц — около 8,3 миллисекунды. Это и есть бюджет времени: за него приложение должно решить, что показать, посчитать положение элементов и передать системе готовую картинку.

Если приложение не уложилось, система показывает предыдущий кадр повторно. Один такой случай глаз не замечает. Несколько подряд — и прокрутка каталога перестаёт быть ровной: появляется рывок, который разработчики называют подтормаживанием, а пользователь описывает словами «приложение тормозит». Отдельно стоит замерший кадр — когда экран не обновляется заметно дольше, чем длится обычная пауза, и приложение выглядит зависшим.

Деловой перевод короче: производительность — это не «приложение быстрое», а «приложение отвечает предсказуемо». Держать под наблюдением имеет смысл три вещи: сколько проходит от нажатия на значок до готового экрана, сколько приложение думает над нажатием кнопки и насколько ровно листаются длинные списки.

Важное разделение, с которого стоит начинать любой разбор: «медленно» бывает разного происхождения. Ожидание ответа сервера и пропуск кадров на устройстве — две разные неисправности с разными исполнителями. Если экран открылся мгновенно, но пустой, дело в обмене данными и в проектировании программного интерфейса. Если экран заполнен, но дёргается при прокрутке, дело в коде приложения.

Что видит пользователь Что происходит технически Где искать причину
Приложение долго открывается после нажатия на значок Холодный старт: подготовка служб до первого кадра Код приложения, порядок запуска, размер сборки
Экран открылся, но остаётся пустым несколько секунд Ожидание ответа сервера, нет промежуточного состояния Серверная часть, кеш, сценарий загрузки экрана
Список дёргается при прокрутке Кадр не укладывается в бюджет времени Перестроения элементов, изображения, графические эффекты
Кнопка нажата, реакция приходит с задержкой Тяжёлая работа выполняется в потоке интерфейса Разбор данных, шифрование, обработка изображений в коде
Приложение замирает на секунду и дольше Замерший кадр: поток занят одной длинной операцией Синхронные операции, чтение файлов, работа с базой на устройстве

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

Бюджет кадра: полоса времени в 16,7 миллисекунды, внутри которой приложение успевает подготовить и нарисовать изображение

Что теряет бизнес, когда приложение подтормаживает

Для интернет-магазина приложение давно перестало быть витриной-дополнением. В кейсе Gulliver Family через приложение проходит 80% мобильного трафика, и оно даёт 50% общего дохода ритейлера. Когда через канал идёт половина выручки, качество прокрутки в каталоге перестаёт быть вопросом вкуса.

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

Есть и техническая сторона, которую видит не только пользователь. Google Play собирает показатели качества с реальных устройств — в том числе долю медленных кадров и долю кадров, на которых приложение подвисает; пороговые значения и способ расчёта опубликованы в документации Android для разработчиков. Приложение с показателями хуже порога получает предупреждение в консоли разработчика и может терять в видимости в каталоге.

По нашему опыту дороже всего подтормаживания обходятся там, где сценарий короткий и повторяется много раз в день: прокрутка каталога, добавление товара в корзину, шаг оформления заказа, сканирование штрихкода прямо в торговом зале. В кейсе «Европа Маркет» — 149 839 пользователей и сканер штрихкода как рабочий инструмент покупателя в зале: здесь задержка отклика ломает не впечатление, а сам сценарий.

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

Как Flutter рисует кадр: два потока и четыре стадии

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

Работа над одним кадром распределена между двумя потоками исполнения:

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

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

Стадия Что происходит Что здесь ломает кадр
Построение Приложение описывает, из каких элементов состоит экран сейчас Перестроение всего экрана вместо изменившейся части, вычисления внутри описания
Компоновка Считаются размеры и позиции всех элементов Глубокая вложенность, списки без ленивой отрисовки, элементы неизвестной высоты
Отрисовка Формируются команды рисования для видеоускорителя Лишние слои, элементы за границами экрана, длинные тени
Растеризация Команды превращаются в готовое изображение на экране Прозрачность, обрезка по форме, размытие, крупные изображения

Первые три стадии выполняет поток интерфейса, четвёртую — поток растеризации. Диагностика начинается с одного вопроса: какой поток не уложился в бюджет. Если перегружен поток интерфейса — причина в коде на Dart: перестроения, вычисления, разбор данных. Если перегружен поток растеризации — причина в графике: полупрозрачные слои, обрезка, тени, размытие фона.

Отдельного упоминания заслуживает движок отрисовки Impeller. Он заранее готовит программы рисования для видеоускорителя (шейдеры), поэтому характерный рывок при первом показе анимации, знакомый по прежнему движку, из типового набора неисправностей ушёл — детали устройства описаны в документации Flutter. Это важно при разборе старых проектов: часть жалоб на «рывки при первом открытии экрана» решается обновлением версии, а не переписыванием кода.

Конвейер кадра во Flutter: построение, компоновка, отрисовка в потоке интерфейса и растеризация в отдельном потоке

Где чаще всего теряются кадры: восемь причин

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

1. Перестраивается весь экран вместо изменившейся части

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

Что помогает: выносить изменяемую часть в отдельный небольшой элемент, помечать неизменные элементы как постоянные (const), выбирать способ управления состоянием так, чтобы обновление доходило только до нужного участка дерева. Проверяется это не рассуждением, а счётчиком перестроений в инструментах разработчика — он показывает, сколько раз каждый элемент был описан заново.

2. Списки без ленивой отрисовки

Каталог на 500 позиций, собранный обычной колонкой внутри прокручиваемой области, строит все 500 карточек сразу — включая те, что находятся далеко за пределами экрана. Открытие такого экрана заметно задерживается, а прокрутка идёт рывками.

Что помогает: списки и сетки с ленивой отрисовкой (ListView.builder, GridView.builder), которые создают только видимые элементы и ближайший запас. Если карточки одинаковой высоты, стоит указать её явно (itemExtent): тогда положение прокрутки считается арифметикой, а не измерением каждого элемента. Для длинных лент дополнительно помогает постраничная подгрузка вместо выдачи всего каталога одним ответом.

3. Изображения в полном разрешении

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

Что помогает: подготовка изображений нужного размера на стороне сервера или сети доставки контента, ограничение размера при разжатии (cacheWidth и cacheHeight), кеширование уже загруженных картинок, современные форматы вместо тяжёлых исходников. Требования к размерам изображений разумно зафиксировать один раз в дизайн-системе проекта, чтобы каждый новый экран не решал этот вопрос заново.

4. Дорогие графические эффекты

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

Что помогает: скругление углов через оформление контейнера, а не через обрезку содержимого; сплошной цвет вместо полупрозрачного слоя там, где визуальной разницы нет; заранее подготовленное изображение вместо эффекта, который пересчитывается в каждом кадре. Общий перечень приёмов собран в рекомендациях по производительности Flutter.

5. Тяжёлые вычисления в потоке интерфейса

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

Что помогает: вынос таких операций в отдельный изолят — параллельный поток исполнения кода на Dart с собственной памятью (compute и Isolate, описание — в документации Flutter). Оговорка: передача данных между изолятами тоже стоит времени, поэтому переносить туда мелкие операции смысла нет — только те, что заметны на шкале кадров.

6. Анимация через перестроение экрана

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

Что помогает: управляющий объект анимации в связке с элементом, который перестраивает только анимируемую часть (AnimationController и AnimatedBuilder), либо готовые неявные анимации. Правило простое: в каждом кадре анимации должно меняться минимальное поддерево, а не экран.

7. Лишние слои и невидимая отрисовка

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

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

8. Сеть и хранилище, замаскированные под подтормаживания интерфейса

Часть жалоб на «тормоза» к отрисовке отношения не имеет: экран ждёт ответ сервера, повторно запрашивает уже полученные данные или синхронно читает файл при открытии. Внешне это выглядит одинаково — приложение не отвечает.

Что помогает: разделить измерение на «сколько ждём сервер» и «сколько рисуем», добавить кеш для справочных данных, показывать промежуточное состояние экрана вместо пустоты. Если ожидание сервера воспроизводится под нагрузкой, проверять его нужно отдельно — нагрузочным тестированием, а не профилировщиком приложения.

Восемь причин потери кадров в мобильном приложении: перестроения, списки, изображения, эффекты, вычисления, анимация, лишние слои, сеть

Холодный старт и размер сборки

Холодный старт — запуск приложения с нуля, когда оно не висит в памяти. Пользователь видит заставку, затем первый экран. Между этими двумя моментами приложение успевает подготовить всё, что записано в коде запуска: аналитику, push-уведомления, локальную базу, загрузку настроек, проверку авторизации.

Типичная ошибка — выполнять всё это до первого кадра. Список служб растёт от выпуска к выпуску, и запуск удлиняется незаметно: каждая новая библиотека добавляет доли секунды, а через год приложение открывается заметно дольше, чем на старте продаж.

Что помогает:

  • до первого кадра выполнять только то, без чего экран нельзя показать; остальное запускать после;
  • показывать первый экран из локального кеша, а свежие данные подгружать поверх — так пользователь видит содержимое, а не пустоту;
  • измерять время запуска в каждом выпуске и сравнивать с предыдущим, а не только при появлении жалоб.

Размер сборки — соседняя тема, влияющая уже не на скорость работы, а на установку: чем тяжелее файл, тем чаще установка обрывается на мобильном интернете. Здесь работают раздача сборки по архитектурам устройств, встроенный разбор состава сборки (flutter build --analyze-size), удаление неиспользуемых шрифтов и наборов значков, сжатие изображений, а для редко используемых разделов — отложенная загрузка компонентов. Приёмы и ограничения собраны в разделе документации Flutter о размере приложения.

Отдельно стоит держать в голове российские магазины приложений: требования к сборкам у них свои, и проверять размер и запуск нужно на всех площадках, где приложение публикуется, — правила публикации описаны в справке RuStore для разработчиков. В кейсе «Сатурн» приложение с каталогом на 30 000+ позиций выходило в четырёх магазинах одновременно, и сборка проверялась под каждый из них.

Чем измерять: режимы сборки и инструменты

Перед инструментами — два правила, без которых измерения бессмысленны.

Первое: измеряют не в отладочном режиме. В отладочной сборке код исполняется иначе и работает заметно медленнее — цифры оттуда завышены и ни о чём не говорят. Профилирование запускают на сборке в режиме профилирования (flutter run --profile), которая по скорости близка к выпускной, но сохраняет служебную информацию.

Второе: измеряют на реальном устройстве, причём на слабом. Эмулятор на рабочем компьютере разработчика не воспроизводит поведение телефона за 12 тысяч рублей, а именно такой аппарат чаще всего у покупателя, который жалуется. Разумный минимум — один флагман и одно бюджетное устройство из статистики вашей аудитории.

Инструмент Что показывает Когда применять
Наложение показателей поверх приложения Два графика: загрузка потока интерфейса и потока растеризации Быстрая проверка «какой поток не успевает» прямо во время прокрутки
Инструменты разработчика Flutter, вкладка производительности Шкала кадров, разбор длительного кадра по стадиям, дерево перестроений Основной разбор: найти конкретный элемент и стадию, где теряется время
Счётчик перестроений Сколько раз каждый элемент был описан заново за сессию Поиск экранов, которые перестраиваются целиком на каждое действие
Режим проверки перерисовки Подсветка областей, нарисованных несколько раз Поиск лишних слоёв и невидимой отрисовки
Системные профилировщики Android Studio и Xcode Память, потоки, энергопотребление, работа с диском Когда причина за пределами отрисовки: утечки памяти, фоновые задачи

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

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

Шкала кадров в инструментах разработчика: длинный кадр разобран по стадиям построения, компоновки, отрисовки и растеризации

Метрики: какие числа держать под наблюдением

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

Показатель Что показывает Как читать
Время холодного старта до первого экрана Через сколько приложение становится пригодным к работе Сравнивать по выпускам; рост от версии к версии — повод разобрать порядок запуска
Доля медленных кадров Насколько ровно листаются экраны у реальных пользователей Медленным считается кадр дольше 16 мс; на экранах 120 Гц порог вдвое ниже
Доля замерших кадров Заметные подвисания приложения Замершим считается кадр дольше 700 мс — это уже видно невооружённым глазом
Доля сессий с отказом приложения отвечать Случаи, когда система предлагает закрыть приложение Отслеживается магазином приложений; влияет на видимость в каталоге
Расход памяти на тяжёлых экранах Риск принудительного закрытия системой на слабых устройствах Смотреть на каталоге и галерее товара, а не на главном экране
Размер сборки по площадкам Влияет на долю завершённых установок Проверять отдельно для каждого магазина приложений

Собираются эти числа штатными средствами площадок и системой сбора показателей приложения: у Google это раздел качества в консоли разработчика, у Apple — сбор показателей с устройств пользователей, описанный в документации MetricKit. Продуктовые показатели — доходимость до оплаты, возвраты, выручка на пользователя — живут в системе аналитики; как их связывают между собой, разобрано в статье об аналитике мобильного приложения.

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

Типичные ошибки при работе над производительностью

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

  1. Оптимизировать по ощущениям. «Кажется, стало быстрее» — не результат. Без замера до и после работа не приёмная, и через два выпуска всё возвращается.
  2. Измерять в отладочном режиме. Самая частая техническая ошибка новичка: цифры завышены, «найденные» узкие места оказываются несуществующими.
  3. Вносить несколько правок за один заход. Непонятно, какая помогла, а какая ухудшила положение. Одно изменение — одно измерение.
  4. Оптимизировать редкий экран. Раздел «О компании» открывают единицы, каталог и корзину — все. Работы сортируются по частоте сценария, а не по удобству исправления.
  5. Списывать подтормаживания на кроссплатформенность. Вывод «дело в технологии» закрывает разбор, не найдя причину. Разбирать нужно конкретный длинный кадр на конкретном экране.
  6. Переносить в изоляты всё подряд. Обмен данными между параллельными потоками тоже стоит времени: на мелких операциях он съедает выигрыш.
  7. Проверять только на флагманах. На быстром телефоне не видно ровно того, из-за чего пишут отзывы.
  8. Не закреплять результат. Без замеров в процессе выпуска и наблюдения в промышленной эксплуатации плавность возвращается к прежнему уровню за несколько релизов.

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

Чек-лист: что проверить на своём приложении

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

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

Чек-лист проверки производительности мобильного приложения: список пунктов с отметками о выполнении

Кто это делает: аудит, своя команда или усиление извне

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

Вариант Когда подходит Где слабее
Разовый аудит Нужно понять, где именно теряется плавность, и получить список работ с приоритетом Аудит не исправляет: после отчёта всё равно нужны исполнители на доработки
Своя команда Есть Flutter-разработчики и запас времени в плане работ Задача конкурирует с продуктовыми; опыт по производительности накапливается медленно, а нужен редко
Усиление команды извне Нужны профильные инженеры на срок работ, без расширения штата Приоритеты и управление задачами остаются на вашей стороне; нужно время на погружение в проект
Передача направления целиком Своей мобильной команды нет, приложение ведёт внешний подрядчик Меньше контроля над ежедневными решениями; выше требования к описанию задач

Flutter — наш профильный стек, поэтому производительность мы разбираем со стороны и разработки, и поддержки. Разовый разбор оформляется как аудит мобильного приложения или аудит кода: на выходе — перечень причин с приоритетом, оценка работ и порядок исправления. Если приложение ведёт ваша команда, а профильных Flutter-инженеров под задачу не хватает, работает аутстаффинг Flutter- и Dart-разработчиков: специалисты подключаются к вашему репозиторию и таск-трекеру, работают по вашим процессам, а продуктом и приоритетами управляете вы. Под ту же задачу при необходимости добираются инженеры по тестированию и инженеры DevOps — последние нужны, когда замеры встраиваются в процесс сборки.

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

Команда под такие задачи у нас распределённая, с центром разработки в Воронеже: аналитик, дизайнер, Flutter- и бэкенд-разработчики, инженеры по тестированию закрепляются за проектом, а не подключаются разово. Для работы над производительностью это существенно: инженер, который уже знает архитектуру приложения, находит причину быстрее, чем специалист, впервые открывший репозиторий.

Кейсы: тяжёлый каталог и мобильный канал продаж

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

DIY-ритейл

«Сатурн»: каталог на 30 000 товаров и четыре площадки

Сеть строительных магазинов в 20+ городах: более 30 000 позиций, колеровка краски, распил материалов, доставка с манипулятором, оформление на юридическое лицо. Каталог такого объёма — как раз тот случай, когда прокрутка и загрузка изображений решают, дойдёт ли покупатель до корзины. Приложение вышло одновременно в четырёх магазинах приложений; результат первого месяца — 7 594 установки и конверсия 12,4%.

Детские товары

Gulliver Family: половина дохода через приложение

Мультибрендовый магазин детских товаров: 4 бренда, 5 направлений. Через приложение проходит 80% мобильного трафика, и оно даёт 50% общего дохода ритейлера. Когда канал приносит половину выручки, требования к отклику и плавности перестают быть вопросом внутренних стандартов — они становятся частью экономики канала.

Продуктовый ритейл

«Европа Маркет»: сканер штрихкода в торговом зале

Сеть гипермаркетов со сценарием «покупатель в зале»: сканирование штрихкода, умный поиск, карта лояльности со скидками до 50%. 149 839 пользователей. В таком сценарии задержка отклика ломает не впечатление, а саму задачу: человек стоит у полки с телефоном в руках и ждёт ответ здесь и сейчас.

Ещё один срез — регулярность выпусков. В кейсе Street Beat за пять месяцев после запуска приложение органически привлекло 130 000 новых пользователей, а команда закрыла 700+ задач и выпустила 7 релизов. Плавность держится именно так: не разовой оптимизацией, а замерами в каждом выпуске. Остальные проекты — в разделе кейсов.

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

Итог: производительность приложения на одной странице

Производительность — не свойство технологии, а результат решений в коде конкретных экранов. Порядок работы короткий:

  1. Сначала измерение, потом исправление. Сборка в режиме профилирования, физическое устройство из вашей статистики, воспроизводимый сценарий.
  2. Определите поток. Не успевает поток интерфейса — причина в коде на Dart; не успевает поток растеризации — причина в графике.
  3. Начните с восьми типовых причин. Перестроения, списки, изображения, эффекты, вычисления, анимация, лишние слои, сеть — они закрывают большую часть случаев.
  4. Сортируйте работы по частоте сценария. Каталог и оформление заказа важнее редких разделов.
  5. Одно изменение — одно измерение. Иначе непонятно, что именно сработало.
  6. Закрепите результат. Замеры перед релизом и наблюдение за долей медленных кадров в промышленной эксплуатации — иначе плавность вернётся к прежнему уровню.

Что делать дальше:

Вопросы и ответы

Почему приложение на Flutter подтормаживает, если код компилируется в машинный?

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

Сколько кадров в секунду считается нормой?

Ориентир — частота обновления экрана устройства: 60 кадров в секунду на большинстве телефонов, 90 или 120 на аппаратах с быстрым экраном. Смотреть стоит не на среднее значение, а на пропуски: приложение со средними 58 кадрами, но регулярными провалами при прокрутке, ощущается хуже стабильных 60. Поэтому в отчётах используют долю медленных кадров, а не среднюю частоту.

Чем медленный кадр отличается от замершего?

Медленным считается кадр, который не уложился в бюджет времени — дольше примерно 16 миллисекунд при 60 Гц. Отдельные медленные кадры глаз не различает, серия подряд читается как рывок при прокрутке. Замершим считается кадр дольше 700 миллисекунд: это уже видимая остановка, когда приложение выглядит зависшим. Причины у них тоже разные: медленные кадры — обычно про перестроения и графику, замершие — про длинную синхронную операцию в потоке интерфейса.

Можно ли оценить производительность без доступа к исходному коду?

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

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

Разбор с отчётом по типовому приложению интернет-магазина — обычно несколько рабочих дней: снять замеры, разложить длинные кадры по стадиям, собрать перечень причин с приоритетом. Сами доработки считаются отдельно и сильно зависят от состояния кода: точечные правки на нескольких экранах занимают дни, переработка структуры каталога или системы управления состоянием — недели. Точный объём называется после разбора, поэтому аудит и доработки разделяют на два этапа.

Влияет ли размер приложения на скорость его работы?

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

Нужно ли переписывать приложение, если оно подтормаживает?

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

Как не растерять результат после оптимизации?

Нужны две вещи. Первая — сценарии-замеры в процессе выпуска: прокрутка каталога и открытие карточки товара проходят перед каждым релизом, а ухудшение показателей останавливает выпуск так же, как непройденный тест. Вторая — наблюдение в промышленной эксплуатации: доля медленных кадров и время холодного старта сравниваются между версиями. Без этого плавность возвращается к прежнему уровню за несколько выпусков, потому что каждый новый экран добавляет свою долю работы.

Материал носит информационно-аналитический характер, отражает оценку команды FITTIN на дату публикации.

ДАВАЙТЕ ОБСУДИМ
ВАШ ПРОЕКТ

Мобильное приложение