Cold Start — це повний цикл запуску Android-застосунку, що починається з нульового стану, коли процес застосунку не існує в пам’яті, а Activity не створювалася. Система створює новий процес, завантажує класи, ініціалізує Application, створює Activity та виконує перше відображення. За даними Google, 2024, холодний запуск на пристроях середнього сегмента може займати від 1 до 5 секунд, і кожні 100 мс затримки знижують ймовірність утримання користувача на 3%.
Головне
Cold Start (холодний запуск) — це сценарій, при якому Android-застосунок запускається з найпочатковішого стану: операційна система створює новий процес (fork з Zygote), виділяє пам’ять, завантажує DEX-код в ART, ініціалізує класи та створює екземпляр Application, а потім першу Activity. До запуску застосунку жодних даних про нього в пам’яті пристрою немає, крім кешованих образів класів, якщо використовується Background Dexopt.
Холодний запуск відбувається в трьох випадках: при першому запуску після встановлення застосунку, при запуску після перезавантаження пристрою та при запуску після того, як система вивантажила процес через нестачу пам’яті. На пристроях з 2–4 ГБ ОЗП система вивантажує фонові процеси досить агресивно, тому Cold Start може відбуватися при кожному поверненні до застосунку через кілька годин простою. В Android 12+ система може зберігати заморожений процес (freeze / cached), але при активній економії пам’яті (OOM-killer) процес буде знищено.
За даними Google (звіт Find My Device, 2023), 65% користувачів закривають застосунок, якщо він не відкривається за 3 секунди. Для соціальних мереж і месенджерів, де користувач повертається десятки разів на день, Cold Start безпосередньо впливає на утримання. В Google Play Console метрика Cold Start входить до розділу Android Vitals і відображається як один із показників ANR та продуктивності. Застосунок, що перевищує поріг “поганого” Cold Start (більше 5 секунд на 25% пристроїв), отримує попередження в консолі та може бути знижений у пошуку.
Android розрізняє три типи запуску застосунку, кожен з яких має різну тривалість, вплив на UX та підходи до оптимізації. Розуміння різниці необхідне для вибору правильної стратегії профілювання.
| Тип запуску | Стан процесу | Application.onCreate | Типовий час |
|---|---|---|---|
| Cold | Немає процесу | Виконується | 1–5 секунд |
| Warm | Процес є, Activity немає | Не виконується | 200–600 мс |
| Hot | Процес + Activity в пам’яті | Не виконується | < 200 мс |
Warm Start відбувається, коли процес застосунку вже існує в фоні, але Activity була знищена (наприклад, користувач повернувся після довгої паузи, і система звільнила пам’ять Activity). Hot Start — коли користувач згортає застосунок і одразу відкриває його знову: Activity призупинена і відновлення займає мінімальний час. Для користувача Cold Start — найпомітніший тип запуску, саме його оптимізація дає найбільший приріст у UX.
Cold Start може стати Warm Start після того, як застосунок було запущено хоча б один раз — ART кешує скомпільовані образи класів (Image в Boot Profile) і повторне завантаження DEX відбувається швидше. Тому другий запуск після першого Cold Start зазвичай на 20–40% швидший. Якщо застосунок використовує Baseline Profiles, профілі завантажуються при першому запуску і другий старт може бути ще швидшим: Google Play, що опублікував Baseline Profiles, прискорив Cold Start на 30% на пристроях з Android 12+.
Cold Start складається зі строго визначених фаз, кожна з яких може бути виміряна та оптимізована незалежно. Знання фаз допомагає визначити, на якому етапі застосунок втрачає час. Google виділяє чотири основні фази: створення процесу, ініціалізація Application, створення Activity та перший кадр.
Система Android (ActivityManagerService) створює новий процес шляхом fork з процесу Zygote. Zygote — це попередньо завантажений процес з common-класами Android. Fork виконується за 30–80 мс — це час, який застосунок не може контролювати. Після fork запускається ActivityThread — екземпляр головного циклу застосунку. На цьому етапі також відбувається завантаження класів через ClassLoader, і ART починає інтерпретувати перший байт-код. Якщо застосунок використовує багато статичних ініціалізаторів, ця фаза може затягнутися.
Відразу після старту ActivityThread викликається Application.onCreate. Тут розробник найчастіше робить помилку, ініціалізуючи все одразу: Crashlytics, Firebase, мережеві клієнти, бази даних, Dagger-компоненти, DI-контейнери. Кожна така ініціалізація — це час, заблокований на головному потоці. Якщо Application.onCreate виконується 500 мс, усі ці півсекунди користувач бачить білий (або чорний) екран. Оптимальна тривалість цієї фази — менше 200 мс на середньому пристрої.
Після ініціалізації Application створюється екземпляр Activity (MainActivity або Launcher Activity). Викликається Activity.onCreate, де відбувається setContentView, ініціалізація фрагментів, налаштування ViewModel, підписка на LiveData/Flow. Якщо onCreate виконує завантаження даних (SharedPreferences, SQLite, API) синхронно на головному потоці, фаза розширюється. Мета — вкластися в 200–400 мс на середньому пристрої.
Після завершення onCreate починається перше відображення: міра, лейаут, draw. Цей момент називається TTFD (Time To First Draw). Якщо застосунок використовує splash-екран (через SplashScreen API на Android 12+ або через theme), відображення може відбутися швидше, але користувач все одно чекатиме, поки splash зникне. Ідеальний TTFD для Cold Start — менше 1.5 секунди.
Вимірювання Cold Start потребує спеціальних інструментів, оскільки звичайне логування (Log.d) починає роботу лише після створення Application, а таймінг fork та завантаження класів залишається недоступним. Google рекомендує три методи: ADB-команди, Android Vitals та кастомні макроси продуктивності.
Найпростіший і відтворюваний спосіб — команда adb shell am start -S -W. Прапорець -S примусово зупиняє застосунок перед запуском (гарантує Cold Start). Команда виводить три метрики: ThisTime (час старту Activity), TotalTime (сумарний час з урахуванням запуску процесу) та WaitTime (час з урахуванням усіх затримок Activity Manager). Для чистоти вимірювання зробіть 5–7 замірів та візьміть медіану — поодинокі заміри піддаються шуму (CPU throttling, фонове навантаження).
# Примусовий Cold Start з заміром
$ adb shell am start -S -W \
com.example.app/.MainActivity
# Вивід команди:
# ThisTime: 1842 ms
# TotalTime: 1842 ms
# WaitTime: 1855 ms
Google Play Console збирає анонімні метрики з усіх пристроїв, де встановлено застосунок. У розділі Android Vitals → Launch time відображається медіанний розподіл Cold Start за моделлю пристрою та версією Android. Це єдиний спосіб побачити реальні показники на пристроях користувачів, а не на тестових девайсах. Якщо на Redmi 9A (2 ГБ ОЗП) Cold Start перевищує 5 секунд, а на Pixel 8 — 1.2 секунди, проблема в обсязі пам’яті та кількості класів. Google також показує користувацьку сприйману затримку на основі 25 перцентиля.
Google Jetpack Macrobenchmark (бібліотека androidx.benchmark) дозволяє писати інструментовані тести запуску застосунку. Тест встановлює застосунок, запускає його з холодним станом і вимірює час до першого кадру. Macrobenchmark автоматично робить 20 прогонів, відкидає викиди та показує стабільні процентилі. Для CI/CD можна порівняти baseline та поточний запуск — якщо час збільшився, CI-пайплайн може падати.
Оптимізація Cold Start — це системна робота, що зачіпає кілька рівнів застосунку: код, ресурси, конфігурацію збірки та архітектуру ініціалізації. Google рекомендує починати з найдорожчого — Application.onCreate — і рухатися до дрібниць.
Перенесіть всю ініціалізацію, що не потрібна на старті, з Application.onCreate у першу точку використання. Firebase, Crashlytics, analytics SDK, push-notifications, DI-компоненти — все можна ініціалізувати після відображення першого екрана. Використовуйте Lazy (by lazy) в Kotlin або ContentProvider-ініціалізацію з явним викликом initialize(context). За даними Google (Android Performance, 2023), лінива ініціалізація скорочує Cold Start на 40–60% для застосунків, що використовують 5+ SDK.
Baseline Profiles — це AOT-компіляція критичних класів та методів, що використовуються на старті застосунку. Без Baseline Profiles ART інтерпретує DEX-код або компілює його JIT, що займає час. З профілями ART компілює зазначені методи в нативний код (AOT) при встановленні застосунку. Google стверджує, що Baseline Profiles дають прискорення Cold Start на 15–40% на Android 9+ і до 60% з ART-оптимізаціями Android 12+. Для створення профілів використовуйте плагін androidx.benchmark:benchmark-baseline-profile-gradle-plugin.
Бібліотека androidx.startup дозволяє впорядкувати ініціалізацію компонентів та виконувати її в одному ContentProvider. Замість кількох ContentProvider’ів від різних бібліотек (кожен додає 1–2 мс до холодного старту) App Startup об’єднує їх у граф залежностей та ініціалізує строго за необхідністю. На старті виконуються лише компоненти з @Initializer, позначені як необхідні для першого екрана. Для решти встановлюється прапорець needEarlyInit = false — вони запускаються після першого відображення.
// App Startup Initializer — ініціалізація після старту
class AnalyticsInitializer : Initializer<Unit> {
override fun create(context: Context) {
Analytics.init(context)
}
override fun dependencies() = listOf<Class<out Initializer<*>>>()
}
// В AndroidManifest.xml позначаємо як необов’язковий
// <meta-data android:name="AnalyticsInitializer"
// android:value="false" />
Розмір DEX-файлу безпосередньо впливає на час його завантаження ART. Використовуйте R8/ProGuard для обфускації та видалення мертвого коду (MinifyEnabled = true). Увімкніть android:extractNativeLibs="false" в маніфесті, щоб APK не розпаковував .so файли при встановленні. Для проєктів з 10+ відстеженнями посилань додавайте startup-priority лише для першого екрана. Кожен зайвий метод в DEX додає 0.5–2 мс до завантаження, а для застосунків з 50k+ методів (multidex з primary dex) — до 300 мс.
Android Vitals в Google Play Console (розділ Launch time) збирає дані з усіх пристроїв, на яких встановлено застосунок, за умови згоди користувача на анонімну діагностику. Метрики діляться на три категорії: “good” (хороший), “moderate” (середній), “bad” (поганий), залежно від часу Cold Start.
Google визначає “bad” Cold Start як час, що перевищує 5 секунд на будь-якому пристрої. Однак на практиці для флагманських пристроїв (Snapdragon 8 Gen) хорошим вважається час менше 1.5 секунд, для середнього сегмента — менше 2.5 секунд, для бюджетного — менше 4 секунд. Android Vitals показує медіану по кожному device model, що дозволяє зрозуміти, на яких пристроях застосунок запускається повільно. Якщо Cold Start поганий на пристроях Samsung A-series або Xiaomi Redmi, причина найчастіше — слабка flash-пам’ять і малий обсяг ОЗП (прискорення через Baseline Profiles дає найбільший ефект саме на таких пристроях).
Окрім відображення в консолі, метрика Cold Start впливає на оцінку якості застосунку в Google Play Search. Застосунки з високим відсотком “bad” запусків отримують позначку “Performance warning” на сторінці встановлення, що знижує конверсію. За даними Google (Android Performance Playbook, 2024), застосунки, що усунули Cold Start-проблеми, в середньому збільшують конверсію встановлення на 5% і покращують показник retention (D1) на 3–7%.
Для більш детального моніторингу використовуйте Firebase Performance Monitoring. Він відстежує Cold Start на рівні сесій, розбиває по версіях застосунку та версіях Android. На відміну від Android Vitals, Firebase показує trace-діаграму витраченого часу по фазах. Наприклад, можна побачити, що на версії 3.2.0 Application.onCreate став займати 800 мс (через нову бібліотеку push-сповіщень), а на версії 3.2.1 — 200 мс (після виправлення).
Нижче наведено два практичні приклади, які безпосередньо прискорюють Cold Start: перенесення ініціалізації SDK після старту та використання SplashScreen API.
Типова помилка — ініціалізація всіх SDK в Application.onCreate. Нижче показано, як перенести некритичну ініціалізацію в корутину, яка запускається після відображення першого кадру. Важливо: Firebase, Crashlytics та Crash Reporting SDK мають бути ініціалізовані на старті — їх відкладати не можна, оскільки вони ловлять краші при ініціалізації інших компонентів. Для решти використовуйте lifecycleScope при першій Activity.
// ❌ Погано — вся ініціалізація в Application.onCreate
class App : Application() {
override fun onCreate() {
super.onCreate()
Firebase.init(this) // критично
Analytics.init(this) // можна пізніше
Database.init(this) // можна пізніше
ImageLoader.init(this) // можна пізніше
}
}
// ✅ Добре — Firebase на старті, решта after inflate
class App : Application() {
override fun onCreate() {
super.onCreate()
Firebase.init(this)
}
}
// В MainActivity після першого кадру:
lifecycleScope.launchWhenResumed {
initializeNonCriticalSdks()
}
На Android 12+ використовуйте офіційний SplashScreen API, який показує system-сплеш (іконка застосунку на темному/світлому фоні) одразу при старті процесу. Це приховує час ініціалізації від користувача — він бачить сплеш, а не білий екран. Для старих пристроїв використовуйте theme-based splash (Theme.SplashScreen в стилях). Важливо: сплеш не повинен тривати довше 300 мс — якщо за цей час застосунок не готовий, намалюйте “постійний” скелетон (shimmer) і показуйте прогрес завантаження.
// SplashScreen API — Android 12+
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition {
isReady.value == false
}
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
// Theme-based splash (Android 5-11)
// В themes.xml:
// <style name="Theme.App.Starting" parent="Theme.SplashScreen">
// <item name="windowSplashScreenBackground">@color/white</item>
// <item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher</item>
// </style>
Часті запитання
Емулятор використовує потужний хост-комп’ютер та емулює процесор з апаратним прискоренням (HAXM / WHPX). Фізичні пристрої, особливо бюджетні (eMMC-пам’ять замість UFS), мають набагато повільніший I/O. Рекомендується вимірювати Cold Start на фізичному пристрої середнього сегмента, щоб отримати реалістичні дані.
За рекомендаціями Google, медіанний Cold Start має бути менше 2 секунд на пристроях середнього сегмента. Для флагманів — менше 1.5 секунд. Для бюджетних пристроїв (2 ГБ ОЗП) допустимо до 4 секунд, але рекомендується оптимізувати до 3 секунд. Значення більше 5 секунд вважаються критичними.
Опосередковано — так. Якщо в маніфесті вказана vector-іконка (AdaptiveIcon), вона має бути скомпільована в drawable при старті. Якщо іконка містить складні шляхи (pathData з десятками кривих), компіляція займає 10–30 мс. Використовуйте VectorDrawable з оптимізованим pathData (через SVGOMG або Android Studio Vector Asset).
Так, якщо Feature Module (Android App Bundle) завантажується на вимогу (on-demand), його Cold Start вважається від моменту натискання на фічу до першого кадру. On-demand модулі завантажуються через Play Core Library, і їх встановлення додає 500–3000 мс до часу старту. Оптимізуйте код фічі так само, як і основний модуль.
Застосунки з більш ніж 64k методів потребують Multidex. Це означає, що ART має завантажити кілька DEX-файлів, що збільшує час Cold Start на 200–800 мс залежно від кількості classes.dex. Використовуйте minSdk 21+ (ART з підтримкою native multidex) та налаштуйте primary dex через --main-dex-list, щоб критичні класи були в першому DEX-файлі.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також