Как ускорить мобильное приложение: запуск, плавность и память
Практика3 минРедакция Innovialabs
Коротко
Сначала измеряйте на реальном устройстве и в релизной сборке, потом оптимизируйте. Чаще всего помогают отложенная инициализация при запуске, кэширование и уменьшение изображений, виртуализация списков и перенос тяжёлой работы с главного потока.
Сначала измерить, потом ускорять
Оптимизация «на глаз» часто ускоряет не то. Порядок работы такой:
- Выберите метрику: время запуска, плавность или память.
- Измерьте на реальном устройстве среднего класса и в релизной (или profile) сборке — debug-сборки заметно медленнее.
- Найдите узкое место профайлером.
- Исправьте одну вещь и измерьте снова.
Что измерять
- Холодный старт — запуск, когда процесса приложения ещё нет в памяти. Самый медленный и самый важный сценарий. Тёплый и горячий старты быстрее, их стоит смотреть отдельно.
- Плавность (jank) — пропущенные кадры. При 60 Гц на кадр есть около 16,7 мс, при 120 Гц — около 8,3 мс. Не уложились — анимация и прокрутка дёргаются.
- Память — пиковое потребление и утечки. Из-за них приложение выгружается системой в фоне или падает.
Инструменты по платформам
| Платформа | Запуск | Плавность | Память |
|---|---|---|---|
| Android | Macrobenchmark, adb, Android Studio Profiler | Profiler, Perfetto, JankStats | Memory Profiler, LeakCanary |
| iOS | Instruments: App Launch, Xcode Organizer | Instruments: Time Profiler, Hitches | Instruments: Allocations, Leaks |
| Flutter | DevTools (profile mode) | DevTools: Performance | DevTools: Memory |
| React Native | Нативные профайлеры платформ | Perf Monitor, профайлер Hermes | Нативные профайлеры |
Быстрая проверка холодного старта на Android:
adb shell am force-stop com.example.app
adb shell am start -W -n com.example.app/.MainActivity
В выводе смотрите TotalTime. Для стабильных цифр используйте Macrobenchmark и несколько прогонов. Для продакшена полезны данные реальных пользователей: Android vitals в Google Play Console и метрики в Xcode Organizer.
Частые исправления
Ускорить запуск
- Отложенная инициализация: аналитика, реклама, чат поддержки и прочие SDK не нужны до первого кадра. Запускайте их после отрисовки или по требованию.
- Не делайте сетевых запросов и тяжёлых операций с базой синхронно при старте. Покажите кэш или скелетон.
- На Android используйте Baseline Profiles — они ускоряют выполнение кода при первом запуске.
- На iOS следите за числом динамически подключаемых библиотек: каждая добавляет работу при загрузке.
Изображения и кэш
- Загружайте изображения в размере отображения, а не в исходном разрешении.
- Используйте библиотеки с дисковым и memory-кэшем: Coil или Glide на Android, Kingfisher или SDWebImage на iOS,
cached_network_imageво Flutter. - Выбирайте эффективные форматы (например, WebP) и сжимайте ассеты.
Виртуализация списков
- Длинные списки отрисовывайте лениво:
RecyclerViewилиLazyColumn,UICollectionViewили ленивые контейнеры SwiftUI,ListView.builderво Flutter,FlatListв React Native. - Задавайте стабильные ключи элементов, чтобы не перерисовывать весь список.
- Упрощайте разметку ячеек: меньше вложенности, меньше тяжёлых эффектов.
Разгрузить главный поток
- Парсинг JSON, работа с базой, обработка изображений — в фоновом потоке: корутины на Android, Swift Concurrency на iOS, изоляты во Flutter.
- В React Native избегайте тяжёлых вычислений в JS-потоке во время анимаций.
- Не пересоздавайте объекты и не пересчитывайте данные при каждой перерисовке.
Память
- Отписывайтесь от слушателей и таймеров, когда экран закрыт.
- Следите за ссылками на экраны из долгоживущих объектов — классическая причина утечек.
- Ограничивайте размер кэшей.
Частые ошибки
- Измерять в debug-сборке или только на флагманском телефоне.
- Инициализировать все SDK в первые миллисекунды.
- Загружать фото с камеры в полном размере в превью списка.
- Оптимизировать без повторного замера и не знать, помогло ли.