Ваше приложение работает безупречно на флагманах — плавный скролл, мгновенный запуск. Но на бюджетных устройствах вроде 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 дней — профилирование, локализация проблем, отчёт. Если нужна ещё и реализация оптимизаций — оцениваем отдельно по объёму правок. Стоимость рассчитывается индивидуально. Закажите аудит производительности, чтобы получить конкретные цифры и план оптимизации. Свяжитесь с нами для оценки вашего проекта.







