Представьте: пользователь открывает приложение, листает ленту — и экран застывает на полсекунды. Касания не проходят, анимация пропадает. Через пару секунд всё приходит в норму, но осадок остаётся. Доля повторных сессий падает, а Crashlytics молчит — ведь формального краша не было. Это классический сценарий App Hang (iOS) или UI Freeze (Android). Мы поможем настроить мониторинг таких зависаний, выявить узкие места и устранить их. Экономия бюджета за счёт сокращения фризов — до 30% нагрузки на main thread.
Почему стандартные краш-репорты не видят зависания?
App Hang и UI Freeze не вызывают исключений — приложение не падает, но перестаёт реагировать на действия пользователя. iOS Watchdog завершает процесс только после 4–8 секунд зависания, Android ANR — через 5 секунд. Короткие фризы 200–500 мс остаются незамеченными, но разрушают впечатление от интерфейса. Стандартные краш-репортеры (Crashlytics, Firebase Crashlytics) фиксируют только ошибки и сигналы, поэтому для обнаружения зависаний необходим отдельный трекинг. Мы используем три подхода: watchdog-поток, метрики кадров и трекинг длительности задач.
Какой порог зависания выставить для продакшена?
Рекомендуем 250 мс для iOS и 200 мс для Android. Меньший порог (100 мс) приведёт к шуму из-за случайных задержек, больший (500 мс) пропустит заметные фризы. При необходимости порог адаптируется под конкретные экраны — например, для сложных списков можно задать 300 мс. Sentry и Datadog позволяют настраивать порог индивидуально. Стоимость настройки окупается за счёт улучшения пользовательского опыта.
Инструменты для обнаружения
| Инструмент | Платформа | Порог | Задержка | Особенности |
|---|---|---|---|---|
| MetricKit | iOS | 250 мс | 24 часа | Встроен в ОС, не требует SDK, но не real-time |
| Sentry App Hang | iOS, Android | от 100 мс | real-time | Watchdog-поток, запись call stack |
| FrameMetricsAggregator | Android | >16 мс | по запросу | Статистика джанки-кадров в продакшене |
| Datadog RUM Long Task | iOS, Android | от 100 мс | real-time | Дашборды и группировка по экранам |
Sentry обнаруживает зависания в реальном времени, тогда как MetricKit — с суточной задержкой. Sentry выявляет фризы в 100 раз оперативнее, что критично для горячих фиксов. MetricKit Documentation подтверждает задержку до 24 часов.
Типичные причины зависаний
| Платформа | Типичная причина | Длительность |
|---|---|---|
| iOS | Декодирование изображений на main thread | 30–80 мс |
| iOS | Синхронная загрузка данных | 100–300 мс |
| Android | Лишние recomposition в Compose | 50–200 мс |
| Android | GC паузы | 10–50 мс |
Эти данные основаны на реальных проектах с аудиторией от 100k пользователей.
iOS — Sentry App Hang Detection
SentrySDK.start { options in options.dsn = "https://[email protected]/project" options.enableAppHangTracking = true options.appHangTimeoutInterval = 0.25 // 250ms } Sentry запускает watchdog-поток, который пингует main thread каждые 100 мс. Если ответа нет дольше appHangTimeoutInterval, снимается стек через backtrace_thread и отправляется как Issue.
Android — FrameMetricsAggregator
val frameMetrics = FrameMetricsAggregator(FrameMetricsAggregator.JANK_DATA) frameMetrics.add(activity) // Позже: val metrics = frameMetrics.metrics metrics?.get(FrameMetricsAggregator.JANK_INDEX)?.let { jankArray -> val jankyFrames = jankArray.size } FrameMetricsAggregator подходит для продакшена: лёгкий, даёт статистику по джанки-кадрам. Для глубокой локальной диагностики используйте Perfetto.
Настройка мониторинга в Datadog
RUM.enable(with: RUM.Configuration( applicationID: "your-rum-app-id", longTaskThreshold: 0.1 // 100ms )) В Datadog дашборде строим виджет:
count:rum.long_task{env:production,service:ios-app} group_by: @view.name visualize_as: top_list Таким образом вы узнаете, на каких экранах больше всего зависаний.
Откуда берутся зависания
На iOS самый частый источник коротких фризов — синхронный вызов на main thread в реакции на UI-событие:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "ProductCell", for: indexPath) as! ProductCell cell.imageView?.image = UIImage(data: product.imageData) return cell } UIImage(data:) синхронно декодирует JPEG/PNG. На iPhone SE с 1900x1200 изображением — 30–80 мс блокировки main thread.
На Android основной виновник Compose-экранов — лишние recomposition в LazyColumn:
@Composable fun ProductList(products: List<Product>) { LazyColumn { items(products) { product -> ProductCard(product) } } } Замена List<Product> на ImmutableList<Product> (kotlinx.collections.immutable) или использование @Stable-аннотации убирает лишние recomposition.
Что входит в работу
- Интеграция Sentry с
enableAppHangTrackingи порогом 250 мс - Настройка MetricKit subscriber для daily diagnostics
- Включение Datadog RUM Long Task tracking с порогом 100 мс
- На Android настройка FrameMetricsAggregator для ключевых Activity
- Построение дашборда по экранам с наибольшим числом Long Tasks
- Анализ stack traces и выявление конкретных виновников
- Письменный отчёт с рекомендациями по оптимизации
Наш опыт
Мы работаем с мобильными проектами более 5 лет. Настроили мониторинг для 20+ приложений с аудиторией от 100k пользователей. Используем современные подходы: MetricKit, Sentry, Datadog, Apple App Hang, Android ANR.
Сроки
Базовая настройка через Sentry и Datadog: 4–8 часов. Полная диагностика с MetricKit и кастомными метриками по экранам: 1–2 дня. Стоимость рассчитывается индивидуально.
Свяжитесь с нами для подбора инструментов под ваш проект. Закажите настройку мониторинга — и получите гарантию производительности.







