Оптимізація часу запуску мобільного застосунку (Cold Start)
Ми часто бачимо, як застосунок втрачає користувачів через довгий запуск. Холодний старт — запуск з нуля, коли процес не існує в пам'яті. ОС створює процес, завантажує бінарник, ініціалізує runtime, запускає Application/AppDelegate та рендерить перший екран. На Android це шлях від іконки до Activity.onResume(), на iOS — до першого кадру. Наш досвід показує, що сповільнення майже ніколи не має однієї причини — це накопичений технічний борг: синхронні ініціалізації SDK, важкі операції в головному потоці, роздутий splash.
Ми пропонуємо комплексний аудит та оптимізацію cold start під ключ. Вартість аудиту стартує від 500$ за одну платформу, а типова економія часу запуску складає 30–50% після впровадження змін. За 1–3 тижні ми виявляємо вузькі місця, впроваджуємо відкладене завантаження, налаштовуємо Baseline Profiles та скорочуємо час запуску до цільових значень. Замовте аудит — ми оцінимо ваш проєкт безкоштовно.
У цій статті розберемо типові проблеми на Android та iOS, покажемо інструменти діагностики та запропонуємо перевірені рішення. Якщо хочете одразу отримати консультацію — пишіть.
Чому cold start критичний для користувацького досвіду?
Дослідження Google показують: 53% користувачів закривають застосунок, якщо він запускається довше 3 секунд. Для ігор та соціальних мереж поріг ще нижчий — 2 секунди. Cold start — перше враження, і воно має бути швидким.
Де втрачається час: 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% на перших запусках після встановлення/оновлення, що в 2-3 рази швидше за JIT-компіляцію без профілів. Це не магія — це явна розмітка «ці класи потрібно скомпілювати заздалегідь».
Як правильно виміряти час холодного старту?
Вимірювати потрібно на реальних пристроях з типовим завантаженням. Емулятор не відображає реальну продуктивність I/O та JIT. Використовуйте системний лог або спеціальні інструменти.
| Інструмент | Платформа | Що показує |
|---|---|---|
| Android Vitals | Android | Агреговані дані по сесіях |
| Perfetto | Android | Події на рівні ядра та застосунку |
| Instruments Time Profiler | iOS | Час до першого кадру |
| os_signpost + DYLD_PRINT_STATISTICS | iOS | Pre-main фаза |
Де втрачається час: 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 с |
Метрики вимірюємо на реальних пристроях, не емуляторі.
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 дозволяють завантажувати лише код, потрібний для першого екрану.
Типові помилки, які ми знаходимо
- Ініціалізація всіх SDK в Application.onCreate() без урахування пріоритетів.
- Синхронне завантаження бази даних (Room/CoreData) на головному потоці.
- Відсутність Baseline Profiles або їх неправильне налаштування.
- Використання емулятора для заміру продуктивності.
Що входить у роботу
- Аудит — профілювання на 3–5 типах пристроїв, виявлення топ-5 вузьких місць.
- Звіт — детальний документ з діаграмами та рекомендаціями.
- Реалізація — впровадження відкладеної ініціалізації, оптимізація графа залежностей, налаштування Baseline Profiles.
- Тестування — повторні заміри на тих самих пристроях, стрес-тести.
- Документація — опис всіх змін та інструкція з підтримки.
- Гарантія — підтримка протягом місяця після здачі проєкту.
Процес оптимізації
Спочатку вимірюємо — без базових метрик незрозуміло, що саме оптимізувати. Profiler сесія на 3–5 типах реальних пристроїв. Далі — аналіз гарячих шляхів, розстановка пріоритетів за внеском у загальний час. Реалізація змін ітераційно з вимірюванням після кожної зміни. Фінальне порівняння до/після на тому ж наборі пристроїв.
Термін роботи — один-три тижні залежно від складності архітектури та кількості платформ.
Наша команда має 10+ років досвіду в мобільній розробці та успішно оптимізувала понад 50 застосунків. Гарантуємо прозорий звіт по кожному етапу.
Зв'яжіться з нами для аудиту вашого застосунку — ми оцінимо проєкт та запропонуємо план оптимізації.







