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 белый экран (blank window) может появиться, если приложение не настроило собственный 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 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

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