Ваше приложение работает безупречно на флагманах — плавный скролл, мгновенный запуск. Но на бюджетных устройствах вроде Xiaomi Redmi 10A или iPhone SE первого поколения начинаются тормоза: выпадающие кадры при скролле, задержки при открытии экранов, необъяснимые паузы. Firebase Crashlytics не фиксирует ошибки — это не крэши, а просадки производительности. App Store Reviews сообщают о проблеме через неделю после релиза, когда лояльные пользователи уже удалили приложение. На основе опыта в 12 проектах по оптимизации мы гарантируем: каждое измерение воспроизводимо и локализовано до конкретного метода. Тестирование производительности мобильного приложения — это не разовая проверка, а системный процесс выявления узких мест и их устранения с измеримыми результатами.
Проблема в том, что стандартные инструменты (Instruments, Android Profiler) дают сырые данные без контекста. Мы не просто запускаем профилировщик — мы воспроизводим реалистичные сценарии: скролл с загрузкой изображений, глубокая навигация, переключение между экранами. Каждая рекомендация подкрепляется числовыми замерами до и после. Например, в одном проекте мы подняли FPS с 35 до 58, а время холодного старта сократили на 1.2 секунды. Получите консультацию, чтобы оценить ваш проект.
Тестирование производительности мобильного приложения: как измерить FPS на iOS
Instruments — основной инструмент для iOS. Мы используем три шаблона:
-
Time Profiler — определяет, где CPU тратит время. Запускаем «тяжёлый» сценарий (скролл, загрузка экрана), смотрим Call Tree с
Invert Call TreeиHide System Libraries. Видим собственный код с процентами нагрузки. -
Core Animation (Rendering) — FPS и причины просадок.
Commit— время формирования слоёв,Render— время GPU. ЕслиCommitвысокий — проблема на main thread. Красная линия в 16.67 ms (60 fps) или 8.33 ms (120 fps, ProMotion) — наглядная граница. -
Allocations — паттерны выделения памяти. Запускаем, совершаем действие, смотрим
Generation Analysis. Если память после Release навигации не падает — утечка.
Пример: экран с коллекцией фото тормозил при скролле. Time Profiler показал 23% времени на UIImage(data:) в cellForItemAt. Синхронная декодировка JPEG на main thread. Решение — ImageIO + kCGImageSourceShouldCacheImmediately: false + декодировка на background queue с DispatchQueue.global(qos: .userInitiated). Время декодирования сократилось с 70 ms до 12 ms, FPS вырос с 35 до 58.
Почему MetricKit — важное дополнение
MetricKit (iOS 13+) собирает production-метрики с реальных устройств пользователей, включая время холодного старта и частоту кадров. Это не синтетические измерения — реальные данные с устройств. Интеграция занимает день и даёт объективную картину производительности в поле.Метрики запуска — тестирование производительности мобильного
MetricKit (iOS 13+) собирает production-метрики с реальных устройств пользователей:
class AppMetricsObserver: NSObject, MXMetricManagerSubscriber { func didReceive(_ payloads: [MXMetricPayload]) { for payload in payloads { if let launchMetrics = payload.applicationLaunchMetrics { // resumeTime — время при background→foreground // timeToFirstDraw — cold start let coldStart = launchMetrics.histogrammedTimeToFirstDraw // отправляем в аналитику } } } } Это не синтетические измерения — реальные данные с устройств пользователей. Дополняет профилирование в Instruments.
Что делать, если кадры падают на Android
В Android Studio — Android Profiler. CPU profiler в режиме Sample Java/Kotlin Methods для общей картины, Trace Java/Kotlin Methods для точного трассирования (с overhead'ом). System Trace показывает взаимодействие с GPU, Choreographer, RenderThread.
Janky frames (кадры > 16 ms): adb shell dumpsys gfxinfo com.example.app | grep "Janky frames". Более 5% janky frames — проблема.
Macrobenchmark — библиотека из Jetpack для воспроизводимых измерений:
@RunWith(AndroidJUnit4::class) class StartupBenchmark { @get:Rule val benchmarkRule = MacrobenchmarkRule() @Test fun startup() = benchmarkRule.measureRepeated( packageName = "com.example.myapp", metrics = listOf(StartupTimingMetric()), iterations = 5, startupMode = StartupMode.COLD, ) { pressHome() startActivityAndWait() } } Запускается на реальном устройстве (не эмуляторе), возвращает timeToInitialDisplay и timeToFullDisplay в миллисекундах. Результаты стабильны между запусками — это измерения, а не «запустили и засекли секундомером». Macrobenchmark даёт в 3 раза более стабильные результаты, чем разовые замеры через adb shell.
Slow rendering: Jetpack Compose
Для Compose — Recomposition счётчик в Layout Inspector. Точнее — ComposeUiTest с measureRepeated:
@Test fun scrollPerformance() { benchmarkRule.measureRepeated( packageName = "com.example.myapp", metrics = listOf(FrameTimingMetric()), iterations = 5, ) { val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) device.findObject(UiSelector().resourceId("com.example.myapp:id/feed_list")) .flingForward() } } FrameTimingMetric собирает данные о каждом кадре: frameOverrunMs — насколько кадр вышел за пределы бюджета.
Flutter: DevTools и flutter_driver
Flutter DevTools → Performance view показывает Frame chart с UI thread и Raster thread. Красные кадры — UI thread занят дольше 16 ms. Жёлтые — Raster thread.
Частая причина красных кадров: setState() перестраивает слишком большое поддерево. Решение — const конструкторы там, где данные не меняются, RepaintBoundary для изолирования перерисовки анимированных элементов.
// Плохо: весь экран перестраивается при каждом тике таймера class CounterScreen extends StatefulWidget { ... } // Лучше: только счётчик изолирован class CounterScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Column(children: [ const HeaderWidget(), // const — не перестраивается CounterWidget(), // только эта часть ребилдится ]); } } Пошаговый план профилирования
- Определяем сценарии (скролл, открытие, background-переключение).
- Запускаем инструмент (Instruments / Profiler / DevTools) и фиксируем базовые метрики.
- Локализуем узкое место (метод, поток, аллокация).
- Вносим исправление и повторяем замеры.
- Сравниваем результаты в таблице.
| Платформа | Инструмент | Ключевая метрика | Типичное узкое место |
|---|---|---|---|
| iOS | Instruments Time Profiler | % времени в main thread | Синхронная декодировка |
| Android | Macrobenchmark StartupTimingMetric | timeToFullDisplay (ms) | Тяжёлый DI-контейнер |
| Flutter | DevTools Frame chart | Кадры >16 ms | Избыточный setState() |
| Типичная проблема | Платформа | Причина | Решение | Результат |
|---|---|---|---|---|
| Пропуск кадров при скролле | iOS | Синхронная декодировка на main thread | Перенос в фоновый поток + кэширование | FPS 35→58 |
| Долгий холодный старт | Android | Инициализация DI на старте | Ленивая загрузка + Hilt с ViewModel | Старт с 2.5с до 1.3с |
| Красные кадры при анимации | Flutter | Избыточный setState() | Использование const + RepaintBoundary | FPS 30→60 |
Что входит в работу
- Профилирование запуска приложения (cold start, warm start)
- Анализ FPS при скролле и навигации
- Поиск утечек памяти через Allocations / Memory Profiler
- Анализ CPU-профиля на тяжёлых операциях
- Macrobenchmark-тесты для Android, MetricKit-интеграция для iOS
- Отчёт с конкретными числами до/после и рекомендациями
Сроки
3–5 дней — профилирование, локализация проблем, отчёт. Если нужна ещё и реализация оптимизаций — оцениваем отдельно по объёму правок. Стоимость рассчитывается индивидуально. Закажите аудит производительности, чтобы получить конкретные цифры и план оптимизации. Свяжитесь с нами для оценки вашего проекта.







