Производительность в мобильной разработке: что это такое, какие метрики и как улучшать

Автор: IT Sectr Опубликовано: 2026-03-25 Время чтения: 12 мин

Медленное приложение — главная причина, по которой пользователи удаляют программы. Доли секунды задержки при запуске или прокрутке списка снижают retention на десятки процентов. Производительность (performance) — это не только скорость, но и стабильность: отсутствие ANR, крашей и утечек памяти. В этой статье разберём все аспекты производительности: от управления памятью (GC, ARC) до профилирования инструментами. Подробнее — в официальном руководстве Android Performance.

Главное

  • ANR и Crash — главные враги пользовательского опыта; предотвращаются фоновыми потоками
  • Memory Leak и Retain Cycle приводят к OOM-крашам; решаются weak-ссылками и утилитами
  • GC (Android) и ARC (iOS) — модели управления памятью; понимание их работы критично
  • Профилирование (Instruments, Android Profiler, LeakCanary) — обязательный этап разработки
  • Cold Start — самая важная метрика запуска; оптимизация Application.onCreate и ленивая инициализация
  • App Size — использовать App Bundle, R8, VectorDrawable и WebP для уменьшения размера

Почему приложение тормозит?

Производительность приложения напрямую связана с торможением (jank) — заметной задержкой между действием пользователя и реакцией интерфейса. Основные причины: блокировка Main Thread (тяжёлые операции на UI-потоке), частые перерисовки layout (overdraw), утечки памяти (частый GC), неоптимальные алгоритмы (O(n²) на больших данных). Frame Rate (FPS) — количество кадров в секунду. Для комфортного опыта нужно стабильные 60 FPS (Android) или 120 FPS (iPhone Pro, iPad Pro). VSync — синхронизация отрисовки с частотой обновления экрана.

Jank возникает, когда отрисовка одного кадра превышает 16.6 мс (для 60 FPS) или 8.3 мс (для 120 FPS). Профилирование GPU (Profile GPU Rendering на Android, Core Animation на iOS) показывает, какие этапы рендеринга занимают больше всего времени. Основные этапы: Layout (расстановка элементов), Draw (отрисовка), Display (передача в буфер кадра). Наиболее частая проблема — layout inflation в XML, особенно при использовании сложных вложенных ConstraintLayout.

Time-to-Interactive (TTI) — время, за которое приложение становится полностью готовым к взаимодействию. TTI включает Cold Start, загрузку данных, инициализацию библиотек. Google рекомендует TTI менее 5 секунд, Apple — менее 2 секунд для основных экранов. Lazy Loading — техника отложенной загрузки контента и библиотек, критичная для улучшения TTI. В IT Sectr мы применяем ленивую инициализацию по умолчанию на всех проектах.

ANR и Crash

ANR и Crash — главные враги производительности мобильного приложения. ANR (Application Not Responding) — диалоговое окно на Android, появляющееся, если главный поток заблокирован более 5 секунд. Причины: синхронные сетевые запросы на UI-потоке, работа с базой данных без корутин, large bitmap decode без downsampling, deadlock на Main Thread. Стек вызовов ANR сохраняется в /data/anr/traces.txt и позволяет определить точное место блокировки.

Crash — неожиданное завершение приложения. На Android — это Exception (Java/Kotlin) или Signal (native code). На iOS — NSException или сигнал (EXC_BAD_ACCESS — обращение к освобождённой памяти). Crash Reporting инструменты: Firebase Crashlytics, Sentry, BugSnag. Они собирают stacktrace, данные об устройстве и шаги воспроизведения. Stack Overflow — переполнение стека вызовов при бесконечной рекурсии. OutOfMemoryError — когда куча (heap) переполнена.

StrictMode — инструмент Android для обнаружения нарушений потоковой безопасности. Позволяет задать правила: ThreadPolicy (запретить диск/сеть на main thread), VmPolicy (обнаружить утечки Activity, SQLite, CloseGuard). StrictMode рекомендуется включать только в debug-сборке — в релизе он не должен работать. На iOS аналог — Main Thread Checker (Xcode), который автоматически выявляет UIKit-вызовы не на главном потоке.

Управление памятью (GC, ARC, Retain Cycle)

Memory Leak (Утечка памяти)

Утечка памяти (Memory Leak) — ситуация, когда объект остаётся в памяти, хотя приложение его больше не использует. Это напрямую снижает производительность приложения. На Android GC (Garbage Collection) не может собрать объект, если на него есть сильная ссылка. Типичные причины: статические ссылки на Activity, неотменённые колбэки/наблюдатели, внутренние классы с неявной ссылкой на внешний класс, Handler с неочищенными сообщениями. LeakCanary — библиотека для автоматического обнаружения утечек.

Retain Cycle (Циклическая ссылка)

ARC (Automatic Reference Counting) — модель управления памятью в iOS. Каждый объект имеет счётчик ссылок (retain count). При обнулении счётчика память освобождается. Retain Cycle — когда два объекта держат сильные ссылки друг на друга (A → B и B → A). ARC никогда не обнулит счётчики. Решение: слабые ссылки (weak) или бесхозные (unowned). Weak автоматически обнуляется (sets to nil) при освобождении объекта. Unowned — не обнуляется, но гарантирует, что объект жив.

GC vs ARC

GC (Garbage Collection) работает на Android (Java/Kotlin). GC периодически приостанавливает выполнение (Stop-the-World pause) для поиска и освобождения недостижимых объектов. GC Trigger: при заполнении кучи (heap) на определённый процент. ARC работает на iOS (Swift/Objective-C) и не имеет пауз — счётчики обновляются атомарно при каждом присваивании. ARC более предсказуем, но может накапливать избыточные retain/release при высокой частоте присваиваний.

Weak Reference и Strong Reference — тип ссылки определяет, может ли GC/ARC освободить объект. Strong Reference — объект не будет собран, пока существует эта ссылка. Weak Reference — GC/ARC может собрать объект; weak-ссылка станет nil (в Swift/Java WeakReference). Unowned Reference (Swift) — не обнуляется при освобождении, доступ к нему после смерти объекта вызывает краш. На Android для слабых ссылок используется java.lang.ref.WeakReference.

Пример обнаружения утечки на Android через LeakCanary:

kotlin
// Утечка: анонимный класс держит ссылку на Activity
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        val handler = object : Handler(Looper.getMainLooper()) {
            override fun handleMessage(msg: Message) {
                // Используем `this@MainActivity`, сохраняя ссылку на Activity
                Log.d("TAG", "Handler received message")
            }
        }
        handler.sendEmptyMessageDelayed(0, 60000)
    }
}

// Исправление: статический Handler + WeakReference
class SafeHandler(activity: MainActivity) : Handler() {
    private val weakActivity =
        WeakReference(activity)

    override fun handleMessage(msg: Message) {
        weakActivity.get() ?: return
        Log.d("TAG", "Handler received message")
    }
}

Профилирование (Instruments, Android Profiler)

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

Инструмент Платформа Что измеряет Когда использовать
Instruments (Time Profiler)iOSCPU, вызовы функций, время выполненияОптимизация алгоритмов, поиск узких мест
Instruments (Allocations)iOSПамять, количество объектов, retain countsПоиск утечек и избыточного потребления памяти
Instruments (Leaks)iOSRetain cycles, утечки памятиРегулярная проверка перед релизом
Android Profiler (CPU)AndroidCPU usage, thread activity, tracesПоиск блокировок Main Thread
Android Profiler (Memory)AndroidHeap dump, allocation trackingПоиск утечек, анализ объектов
Android Profiler (Network)AndroidТрафик, скорость, тайминги запросовОптимизация сетевых вызовов
LeakCanaryAndroidАвтоматическое обнаружение утечек памятиНа всех этапах разработки
StrictModeAndroidДиск/сеть на main thread, утечкиDebug-сборка
Traceview / SystraceAndroidМетод-трассировка, системные событияГлубокий анализ задержек

Instruments (Xcode) — мощнейший инструмент для iOS. Time Profiler показывает, какие функции потребляют больше всего CPU. Allocations отслеживает создание и освобождение объектов. Leaks автоматически находит retain cycles. Шаги профилирования: (1) запустить Instruments; (2) выбрать шаблон (Time Profiler для CPU); (3) выполнить проблемный сценарий; (4) проанализировать стек вызовов — самый широкий столбец — самая «горячая» функция.

Android Profiler — встроенный в Android Studio (View → Tool Windows → Profiler). CPU Profiler показывает загрузку каждого потока. Memory Profiler — heap dump и allocation tracking. Network Profiler — все HTTP-запросы с таймингами. Energy Profiler — потребление энергии: WakeLock, Location, Network. Для детальной трассировки используется Systrace (Android 10+) или Perfetto — системный трейсинг с точностью до микросекунд.

Запуск приложения (Cold/Warm/Hot Start)

Запуск приложения — один из ключевых показателей производительности. Он делится на три типа: Cold Start — приложение запускается с нуля: процесс создаётся, Application.onCreate (Android) / AppDelegate.applicationDidFinishLaunching (iOS), загрузка классов, инициализация библиотек. Warm Start — процесс существует, но Activity/ViewController уничтожен (например, при повороте экрана или возврате из памяти). Hot Start — Activity/ViewController в памяти, приложение просто показывается (переключение с другого приложения).

Cold Start — самая важная метрика. На Android включает: (1) launch Activity — загрузка XML, инициализация View; (2) первый frame — время до первой отрисовки. Google рекомендует: launch Activity < 200 мс, первый frame < 500 мс, TTI < 5 секунд. Оптимизация Cold Start: уменьшить Application.onCreate (корутины для ленивой инициализации), использовать SplashScreen API (Android 12+), отложить инициализацию библиотек (WorkManager, DI), удалить лишние ContentProviders.

На iOS Cold Start включает: загрузка Mach-O бинарного файла, dyld (динамический линковщик), инициализация Objective-C runtime, Application delegate, первый controller. Chrome Custom Tabs (Android) и Universal Links (iOS) — технологии для быстрого открытия внешнего контента в приложении без полного Cold Start. Рекомендуется тестировать Cold Start на реальных устройствах среднего сегмента.

Оптимизация размера

Размер приложения — фактор производительности установки и обновлений. Он влияет на конверсию: каждые 10 MB снижают конверсию на 1%. Google Play рекомендует размер APK менее 150 MB; App Store — менее 200 MB (сотовые сети — 100 MB). Основные методы оптимизации: сжатие изображений (WebP вместо PNG даёт 25-35% экономии), векторизация (VectorDrawable в Android, SF Symbols в iOS), удаление неиспользуемого кода (R8/ProGuard), удаление неиспользуемых ресурсов (lint → unused resources).

App Bundle (Android) — формат публикации, при котором Google Play генерирует оптимизированный APK под каждое устройство. App Bundle уменьшает размер загрузки на 20-40%. Dynamic Delivery — модули, которые загружаются по требованию (on-demand feature modules). На iOS эквивалент — On-Demand Resources (ODR): ресурсы, загружаемые после первого запуска (уровни игры, видео).

Lazy Loading — техника, при которой модули и библиотеки не загружаются при запуске, а подгружаются по необходимости. Split APK (Android) и App Slicing (iOS) — разделение приложения на архитектурные слоты: arm64-v8a, x86_64. App Size Optimization — постоянный процесс: анализируйте состав APK (Analyze APK в Android Studio), удаляйте дубликаты иконок, используйте SVG вместо нескольких плотностей PNG. В IT Sectr мы включаем проверку размера сборки в CI/CD для каждого MR.

Часто задаваемые вопросы

Что такое ANR и как его избежать?

ANR (Application Not Responding) — диалог, который появляется на Android, если главный поток заблокирован более 5 секунд. Чтобы избежать ANR, выносите все тяжёлые операции (сеть, база данных, обработка файлов) в фоновые потоки. На iOS аналог — frozen UI, когда приложение перестаёт реагировать на касания.

Что такое Memory Leak и Retain Cycle?

Memory Leak — утечка памяти, когда объект не может быть освобождён, потому что на него остаются ссылки. Retain Cycle — ситуация в iOS/Objective-C, когда два объекта ссылаются друг на друга (A → B → A), и ARC не может освободить ни один. Решение: weak/unowned ссылки и своевременная очистка колбэков.

Какие инструменты использовать для профилирования?

Для iOS: Instruments (Time Profiler, Allocations, Leaks). Для Android: Android Profiler (CPU, Memory, Network), LeakCanary (утечки памяти), StrictMode (нарушения потоков). Рекомендуется комбинировать профилирование на этапе разработки и интеграции.

Чем отличается Cold Start от Warm Start и Hot Start?

Cold Start — приложение запускается с нуля: процесс создаётся, классы загружаются, Application.onCreate выполняется. Warm Start — процесс существует, но Activity/ViewController пересоздаётся. Hot Start — Activity/ViewController уже в памяти, просто показывается. Cold Start самый долгий (1-5 секунд) и критичен для пользовательского опыта.

Как уменьшить размер мобильного приложения?

Основные методы: удаление неиспользуемых ресурсов и кода (use R8/ProGuard), векторизация изображений (VectorDrawable, SF Symbols), компрессия PNG/WebP (Android), App Bundle вместо APK, удаление лишних библиотек, Lazy Loading модулей. Оптимизация размера может сократить APK на 40-60%.

Итоги

  • ANR и Crash — главные проблемы стабильности; решаются фоновыми потоками и crash-репортерами
  • Memory Leak и Retain Cycle — основные причины OOM; решаются weak-ссылками и LeakCanary
  • GC (паузы Stop-the-World) vs ARC (без пауз, но retain cycles) — разные модели памяти
  • Профилирование — обязательный этап: Instruments (iOS), Android Profiler, LeakCanary, StrictMode
  • Cold Start — ключевая метрика; оптимизация Application.onCreate и ленивая инициализация
  • App Bundle и WebP/VectorDrawable — основные инструменты для уменьшения размера на 20-60%
  • Производительность — непрерывный процесс, а не разовая активность; внедряйте метрики в CI/CD

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

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект