Оптимізація часу запуску мобільної програми (Cold Start)
Cold start — запуск програми з нуля: процес не існує в пам'яті, ОС створює процес, завантажує бінарник, ініціалізує runtime, запускає Application/AppDelegate, рендерить перший екран. На Android це шлях від натискання іконки до Activity.onResume(). На iOS — від tap до першого відрисованого кадру.
Сповільнення cold start майже ніколи не має одну причину. Це накопичений борг: ініціалізації SDK у Application.onCreate(), важкі операції на main thread, раздутий 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) — синхронно. Аналітика, push — через 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 типах реальних пристроїв. Далі — аналіз гарячих шляхів, розстановка пріоритетів за внеском в загальний час. Реалізація змін ітеративно з вимірюванням після кожної зміни. Фінальне порівняння до/після на тому ж наборі пристроїв.
Термін роботи: одна-три тижні залежно від складності архітектури та кількості платформ.







