Оптимизация времени запуска мобильного приложения (Cold Start)
Cold start — запуск приложения с нуля: процесс не существует в памяти, ОС создаёт процесс, загружает бинарник, инициализирует runtime, запускает Application/AppDelegate, рендерит первый экран. На Android это путь от нажатия иконки до Activity.onResume(). На iOS — от tap до первого отрисованного кадра.
Замедление cold start почти никогда не имеет одну причину. Это накопленный долг: инициализации SDK в Application.onCreate(), тяжёлые операции в главном потоке, раздутый splash экран, синхронные запросы к базе данных при старте.
Где теряется время: Android
Android Vitals в Play Console показывает метрику «Startup time» — процент сессий с cold start > 5 секунд. Но это агрегат. Для диагностики нужен Android Studio Profiler → App Startup или Perfetto.
Типичная картина при аудите: Application.onCreate() занимает 800-1200 мс на mid-range устройстве, и большая часть этого — синхронная инициализация Firebase, Amplitude, AppsFlyer, OneSignal и ещё трёх SDK. Каждый из них внутри делает SharedPreferences.read, создаёт HandlerThread, регистрирует BroadcastReceiver.
Решение: App Startup Library (androidx.startup) с явным графом зависимостей инициализаций. SDK, которые нужны немедленно (Crashlytics) — синхронно. Аналитика, пуши — через ContentProvider-ленивую инициализацию или в фоновом потоке с задержкой в 2-3 секунды после первого рендера.
Второй источник потерь — Dagger/Hilt граф зависимостей при старте. Если @Singleton-компоненты тяжёлые (Room database, Retrofit инстансы) и создаются все сразу — это видно как провал в Profiler сразу после onCreate. Решение: @Lazy<T> для компонентов, которые не нужны на первом экране, и backgroundScope.launch для инициализации репозиториев.
Baseline Profiles (Jetpack) — предварительная компиляция горячих путей кода в AOT до того, как их увидит JIT. ProfileInstaller + BaselineProfileRule в тестах позволяют сократить cold start на 30-40% на первых запусках после установки/обновления. Это не магия — это явная разметка «эти классы нужно скомпилировать заранее».
Где теряется время: iOS
os_signpost + Instruments Time Profiler — единственный правильный способ увидеть реальную картину. Xcode показывает время от tap до applicationDidFinishLaunching, и отдельно — время до первого значимого рендера.
Главные виновники на iOS: +load методы в Objective-C классах и C++ статические конструкторы. Они выполняются до main(), и их время не видно в обычном Profiler без специальной разметки. DYLD_PRINT_STATISTICS в переменных среды покажет реальное время pre-main фазы.
Swift инициализация быстрее Obj-C, но и здесь есть ловушки: @UIApplicationMain класс с тяжёлым init, синглтоны через static let shared = ... которые создаются в application(_:didFinishLaunchingWithOptions:) цепочкой.
URLSession, CoreData stack, Keychain — всё это должно инициализироваться лениво или в фоне. CoreData NSPersistentContainer.loadPersistentStores — асинхронный по умолчанию, но разработчики часто оборачивают его в семафор, превращая в синхронный вызов на main thread.
Метрики и целевые значения
| Тип устройства | Хорошо | Приемлемо | Плохо |
|---|---|---|---|
| Android high-end | < 1.0 с | 1.0–2.0 с | > 2.0 с |
| Android mid-range | < 2.0 с | 2.0–4.0 с | > 4.0 с |
| iPhone (последние 3 поколения) | < 0.8 с | 0.8–1.5 с | > 1.5 с |
| iPhone (5+ лет) | < 1.5 с | 1.5–3.0 с | > 3.0 с |
Метрики измеряем на реальных устройствах, не эмуляторе. Эмулятор не отражает реальную производительность I/O и JIT.
Flutter и React Native
В Flutter cold start упирается в инициализацию Dart VM и движка. FlutterActivity vs FlutterFragmentActivity — разница в 50-100 мс. Предварительная инициализация движка через FlutterEngineCache + FlutterEngineGroup позволяет переиспользовать движок между запусками. Splash screen через flutter_native_splash правильно синхронизирован с нативным launch screen.
В React Native проблема — время загрузки JS bundle. Hermes engine (сборка в байткод) сокращает parse-time до 2-3x по сравнению с JSC. RAM Bundles и inline requires позволяют загружать только код, нужный для первого экрана.
Процесс оптимизации
Сначала измеряем — без базовых метрик непонятно, что именно оптимизировать. Profiler сессия на 3-5 типах реальных устройств. Дальше — анализ горячих путей, расстановка приоритетов по вкладу в общее время. Реализация изменений итерационно с измерением после каждого изменения. Финальное сравнение до/после на том же наборе устройств.
Срок работы — одна-три недели в зависимости от сложности архитектуры и количества платформ.







