Warm Start: суть, теплий запуск і оптимізація в Android

Автор: IT Sectr Опубліковано: 2026-03-31 Час читання: 8 хв

Warm Start — це сценарій запуску Android-додатку, при якому процес додатку вже існує в пам'яті (наприклад, після згортання), але Activity була знищена системою для економії ресурсів. Application.onCreate вже виконано, класи завантажено, але UI створюється заново. За даними Google, 2024, Warm Start займає від 200 до 800 мс і становить близько 40% всіх запусків на пристроях з 4 ГБ ОЗП.

Головне

  • Warm Start — запуск додатку з існуючим процесом, але без Activity в пам'яті
  • Відмінність від Cold Start: Application.onCreate не виконується, класи вже завантажено
  • Час Warm Start становить 200–800 мс проти 1–5 секунд для Cold Start
  • Сценарії: повернення в додаток через кілька годин, вивантаження Activity OOM-killer'ом
  • Оптимізація зосереджується на збереженні стану Activity та кешуванні даних

Що таке Warm Start

Warm Start — це стан між Cold Start і Hot Start: процес додатку існує в пам'яті (іноді в фоновому кеші Linux), але Activity неактивна і буде створена заново. Коли Android не вистачає оперативної пам'яті, він може вивантажити Activity зі стеку, залишивши процес живим. Коли користувач повертається в додаток, відбувається Warm Start: створюється новий екземпляр Activity, виконуються lifecycle-методи onCreate → onStart → onResume, але Application.onCreate і завантаження класів пропускаються.

Причини Warm Start

Android приймає рішення про вивантаження Activity на основі пріоритету процесу (importance rank). Activity у фоні (рівень PROCESS_STATE_IMPORTANT_FOREGROUND або PROCESS_STATE_TOP_SLEEPING) може бути знищена через 5–30 хвилин після згортання додатку, залежно від доступної ОЗП. На пристроях з 3 ГБ ОЗП Activity може бути вивантажена вже через 10 хвилин, на пристроях з 8 ГБ ОЗП — через кілька годин. Важливо: при Warm Start onSaveInstanceState викликається до знищення Activity, і розробник може зберегти стан UI.

Сприйняття користувачем

Користувач не бачить різниці між Warm і Cold Start — він просто натискає на іконку додатку і чекає. Однак при Warm Start білий екран може з'явитися, якщо додаток не налаштував власний theme для стартового вікна. Google рекомендує встановити кастомну тему в маніфесті (Theme.AppCompat.Light або Theme.Material3.DayNight) для стартової Activity, щоб уникнути мерехтіння білого/чорного екрану при Warm Start. На Android 12+ SplashScreen API також приховує цей ефект.

Warm Start vs Cold Start vs Hot Start

Розуміння різниці між трьома типами запуску необхідне для вибору правильної стратегії профілювання та оптимізації. Кожен тип має свою тривалість, свої вузькі місця та свої інструменти вимірювання.

КритерійCold StartWarm StartHot Start
ПроцесСтворюється зановоІснує в пам'ятіІснує в пам'яті
Application.onCreateВиконуєтьсяНе виконуєтьсяНе виконується
ActivityСтворюється з нуляСтворюється з нуляВідновлюється зі стеку
Час1–5 секунд200–800 мс< 200 мс
onCreate ActivityПовнийПовний (з restore)Пропускається

На практиці Warm Start становить від 30% до 60% всіх запусків додатку, залежно від звичок користувача та об'єму ОЗП пристрою. Користувачі, які тримають багато додатків відкритими (multitasker), частіше стикаються з Warm Start. Для соціальних мереж і месенджерів Warm Start — найчастіший сценарій, оскільки додаток весь час у фоні. Для банківських додатків, навпаки, переважає Cold Start (примусове очищення процесу з міркувань безпеки).

Фази теплого запуску

Warm Start складається з трьох фаз, кожна з яких може бути виміряна та оптимізована. На відміну від Cold Start, тут немає фази fork і завантаження класів, але є фаза відновлення стану (restore), яка може бути дорогою.

Фаза 1: Стартове вікно (window background)

Система перевіряє, чи є у додатку theme для стартового вікна. Якщо тема не встановлена, відображається білий (або чорний, залежно від системи) екран. Якщо тема встановлена, відображається background з теми. Ця фаза займає 10–30 мс, але візуально відчутна, якщо тема не збігається з реальним UI додатку. Використовуйте Theme.Material3.DayNight з кастомним windowBackground, колір якого збігається з фоном першого екрану — це створює ефект миттєвого завантаження.

Фаза 2: Створення Activity (restore)

Система викликає onCreate з передачею Bundle savedInstanceState, який був збережений в onSaveInstanceState перед знищенням Activity. Якщо додаток правильно зберіг стан (текст полів, позицію скролу, дані ViewModel), відновлення відбувається швидко. Якщо ні — Activity починається з чистого аркуша і користувач бачить лоадер, поки дані підвантажуються. Ключовий момент: ViewModel-об'єкти переживають Warm Start тільки в тому випадку, якщо процес не був знищений — при Warm Start ViewModel зберігається в пам'яті.

Фаза 3: Перший кадр (TTFD)

Після onCreate виконується onStart → onResume, і система викликає першу відрисовку. TTFD (Time To First Draw) для Warm Start повинен бути менше 300 мс на середньому пристрої. Якщо перший екран містить складний RecyclerView з важкими View або завантажує зображення по мережі, TTFD може перевищити поріг. Використовуйте Placeholder та Shimmer для плавного завантаження контенту після першого кадру.

Як виміряти Warm Start

Вимірювання Warm Start складніше ніж Cold Start, оскільки потрібно симулювати стан «процес живий, Activity знищена». Стандартна команда ADB з прапорцем -S не підходить — вона вбиває процес. Для Warm Start використовуйте інші підходи.

ADB shell am start без -S

Спочатку запустіть додаток через adb shell monkey або tap на іконку, потім згорніть його (adb shell input keyevent 3 keyevent HOME). Зачекайте 5–10 секунд, щоб система могла вивантажити Activity, і запустіть adb shell am start -W (без -S). Команда поверне час старту, який буде коротшим за Cold Start. Для відтворюваності використовуйте скрипт: запустити → почекати → home → почекати → запустити.

bash
# Симуляція Warm Start через ADB
$ adb shell am start -W \
    com.example.app/.MainActivity

# Вивід (Warm Start):
# ThisTime: 412 ms
# TotalTime: 412 ms
# WaitTime: 423 ms

Macrobenchmark для Warm Start

Бібліотека androidx.benchmark.macro підтримує вимірювання Warm Start. Для цього в тесті встановіть startupMode = StartupMode.WARM — бібліотека запустить додаток, згорне його, почекає (configurable delay), а потім виміряє повторний запуск. Macrobenchmark робить 10–20 прогонів і обчислює процентилі. В CI/CD можна налаштувати поріг: якщо P50 Warm Start перевищує 600 мс — тест падає. Це дозволяє відстежувати регресії при кожному коміті.

Firebase Performance Monitoring

Firebase автоматично розрізняє Cold і Warm Start на основі часу з моменту попереднього закриття додатку. Якщо додаток було відкрито протягом останніх 30 хвилин, Firebase класифікує запуск як Warm. У консолі Firebase ви побачите окремі графіки для кожного типу старту, що дозволяє оцінити ефективність оптимізацій. Наприклад, після впровадження збереження стану в ViewModel можна побачити зниження Warm Start на 30%.

Як оптимізувати Warm Start

Оптимізація Warm Start зосереджується на двох напрямках: прискорення Activity.onCreate і правильне відновлення стану. Оскільки Application.onCreate і завантаження класів вже виконані, головне вузьке місце — UI-код першого екрану.

Асинхронне відновлення стану

Якщо збережений стан (savedInstanceState) містить дані, які потрібно десеріалізувати (Bitmap, String, JSON), робіть це в фоновому потоці. Замість прямого читання з Bundle в onCreate запустіть корутину і покажіть shimmer-екран. На практиці десеріалізація Bundle на середньому пристрої займає 20–100 мс — здається небагато, але для Warm Start це 10–50% всього часу. Використовуйте Saved State Module бібліотеки Jetpack, яка автоматично зберігає та відновлює стан ViewModel в Bundle або базі даних.

Оптимізація setContentView

Роздуття XML-макету (layout inflation) — один з найдорогих етапів Warm Start. Якщо перший екран використовує складний CoordinatorLayout з AppBar, CollapsingToolbar, NestedScrollView плюс три RecyclerView, час inflation може досягати 300 мс. Рішення: використовуйте ConstraintLayout для плоскої ієрархії, застосовуйте ViewStub для невидимих на старті секцій (bottom sheet, dialog), вмикайте асинхронне роздуття для важких фрагментів через AsyncLayoutInflater. В Jetpack Compose inflation не потрібен, але компіляція Compose-дерева на Warm Start може займати аналогічний час.

Кешування даних

При Warm Start дані, які додаток завантажував у попередню сесію, можуть бути вже в кеші: Room-база даних, SharedPreferences, in-memory кеш в ViewModel. Якщо ваш перший екран показує список з сервера, перевіряйте кеш при старті та оновлюйте дані у фоні. Використовуйте стратегію cache-then-network: спочатку відобразити кешовані дані (миттєво), потім оновити з сервера (асинхронно). Це скорочує сприйнятий час Warm Start до 100–200 мс.

kotlin
// ViewModel з кешуванням для Warm Start
class FeedViewModel : ViewModel() {
    private val cache = MutableStateFlow<List<Item>>(emptyList())

    init {
        // Спочатку кеш, потім мережа
        viewModelScope.launch {
            cache.emit(db.getItems()) // Warm Start: дані вже в БД
            cache.emit(api.fetchItems()) // Оновлення у фоні
        }
    }
}

Збереження стану при Warm Start

Правильне збереження стану — ключовий фактор, що відрізняє хороший Warm Start від поганого. Користувач очікує повернутися в додаток і побачити те ж саме, що залишив — включаючи позицію скролу, текст в полях, вибрані вкладки.

onSaveInstanceState

Система викликає onSaveInstanceState при знищенні Activity, але ДО того, як процес може бути вбитий. В Bundle зберігаються тільки прості дані (String, Int, Parcelable, Serializable). Для складних даних використовуйте SavedStateHandle в ViewModel — він автоматично зберігає та відновлює поля при Warm Start. На відміну від onSaveInstanceState, SavedStateHandle працює навіть якщо процес пережив Warm Start (ViewModel не знищується). Приклад: для тексту в EditText використовуйте SavedStateHandle.getLiveData(«text») — текст збережеться та відновиться автоматично.

ViewModel і Warm Start

Якщо при Warm Start процес не був убитий, ViewModel залишається в пам'яті і onCleared не викликається. Це означає, що всі дані, завантажені в попередній сесії, доступні миттєво. Однак якщо процес був убитий (пристрій в deep sleep більше 30 хвилин), ViewModel знищується і створюється заново з SavedStateHandle. Для коректної роботи ViewModel при Warm Start використовуйте SavedStateHandle з полями, які потрібно відновити за будь-якого сценарію. Різниця: ViewModel з @HiltViewModel підтримує SavedStateHandle автоматично.

МеханізмПроцес живийПроцес убитий
ViewModelДані в пам'ятіЗнищена, створюється заново
SavedStateHandleДані в пам'ятіВідновлюються з Bundle
onSaveInstanceStateВикликається при вивантаженні ActivityНе викликається
Room DBКеш доступнийКеш доступний (диск)

Збереження прокрутки RecyclerView

Одна з найчастіших проблем Warm Start — втрата позиції скролу. Користувач прогорнув стрічку до 50-го елементу, згорнув додаток, повернувся — і бачить початок списку. Рішення: зберігайте layoutManager.onSaveInstanceState (зберігає позицію та offset першого видимого елементу) і відновлюйте його в onRestoreInstanceState. Також можна зберігати останню видиму позицію в SharedPreferences з ключем по даті/часу, щоб при Warm Start швидко відновити позицію.

kotlin
// Збереження позиції скролу RecyclerView
override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putParcelable(
        "rv_state", binding.recyclerView
            .layoutManager?.onSaveInstanceState()
    )
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    savedInstanceState?.getParcelable<Parcelable>("rv_state")
        ?.let { binding.recyclerView.layoutManager?.onRestoreInstanceState(it) }
}

Приклади коду для Warm Start

Два практичних приклади оптимізації Warm Start: використання SavedStateHandle в ViewModel і асинхронне відновлення складних даних після старту.

ViewModel з SavedStateHandle

SavedStateHandle автоматично зберігає поля в Bundle і відновлює їх при Warm Start. Поле профілю користувача (String, JSON) буде відновлено без зайвих запитів до сервера. Якщо процес був убитий, SavedStateHandle завантажить останній збережений стан з Bundle.

kotlin
class ProfileViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    val profile: StateFlow<Profile?>
        get() = savedStateHandle
            .getStateFlow("profile", null)

    fun loadProfile(id: String) {
        viewModelScope.launch {
            savedStateHandle["profile"] =
                api.getProfile(id)
        }
    }
}

// Warm Start: profile не null, UI без лоадера
// Після завантаження: profile оновлюється в SavedStateHandle

AsyncLayoutInflater для важкого екрану

Якщо перший екран містить складний макет (карта, градієнт, кілька списків), використовуйте AsyncLayoutInflater для роздуття важких елементів у фоні. Поки макет роздувається, покажіть placeholder з shimmer-ефектом. Це особливо важливо для Warm Start, де кожна мілісекунда на рахунку. AsyncLayoutInflater працює на фоновому потоці і передає готовий View в callback на головному потоці.

kotlin
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Placeholder-макет для миттєвої відрисовки
        setContentView(R.layout.placeholder_shimmer)

        // Асинхронне завантаження важкого макету
        AsyncLayoutInflater(this).inflate(
            R.layout.activity_main_complex,
            findViewById(R.id.container)
        ) { view, resId, parent ->
            parent?.removeAllViews()
            parent?.addView(view)
        }
    }
}

Часто задавані питання

Чи може Warm Start перейти в Cold Start?

Так, якщо в момент Warm Start система вирішить убити процес додатку (наприклад, для звільнення пам'яті під інший додаток), запуск стане Cold Start з нуля. Це трапляється на пристроях з 2–3 ГБ ОЗП при одночасній роботі кількох додатків. Фактично Warm Start гарантований тільки протягом 10–20 хвилин після згортання на пристроях середнього класу.

Чи зберігається ViewModel при Warm Start?

Так, якщо процес не був убитий, ViewModel зберігається в пам'яті і onCleared не викликається. Це ключова перевага Warm Start: всі дані, завантажені мережеві запити, кеш в ViewModel — доступні миттєво. Якщо процес був убитий, ViewModel створюється заново через ViewModelProvider.Factory або @HiltViewModel, і SavedStateHandle відновлює збережені поля.

Чому Warm Start може бути повільнішим за Cold Start?

Теоретично Warm Start завжди швидший за Cold Start, але на практиці є сценарії, коли різниця мінімальна: якщо Application.onCreate був легким (50 мс), а Activity.onCreate — важким (800 мс), то Warm Start (800 мс) майже дорівнює Cold Start (850 мс). У цьому випадку оптимізувати потрібно не Application, а Activity.onCreate — саме він стає вузьким місцем для Warm Start.

Як SplashScreen API впливає на Warm Start?

SplashScreen API на Android 12+ показує системний сплеш (іконка на кольоровому фоні) відразу при старті — і для Cold, і для Warm Start. Для Warm Start сплеш відображається всього 100–300 мс, після чого його змінює UI додатку. SplashScreen не прискорює сам запуск, але маскує час створення Activity, покращуючи сприйняття.

Чи потрібно оптимізувати Warm Start, якщо Cold Start вже швидкий?

Так, тому що Warm Start трапляється в 2–3 рази частіше, ніж Cold Start. Якщо Cold Start займає 1.2 секунди, а Warm Start — 600 мс, то 40% запусків (Warm) все ще займають 0.6 секунди, що відчутно. Оптимізація Warm Start до 200–300 мс дає користувачеві відчуття миттєвого повернення. На пристроях з 6+ ГБ ОЗП Warm Start може становити до 80% всіх запусків, і його оптимізація стає пріоритетом.

Підсумки

  • Warm Start — запуск додатку з існуючим процесом, без Activity в пам'яті, час 200–800 мс
  • Головна відмінність від Cold Start: Application.onCreate не виконується, класи завантажено
  • Три фази Warm Start: стартове вікно → створення Activity → перший кадр
  • Вимірюється через ADB без прапорця -S або Macrobenchmark з StartupMode.WARM
  • Оптимізація: SavedStateHandle, AsyncLayoutInflater, cache-then-network, ConstraintLayout
  • ViewModel зберігається при Warm Start (процес живий) — дані доступні миттєво
  • Warm Start становить 40–80% всіх запусків додатку

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також