Warm Start — это сценарий запуска Android-приложения, при котором процесс приложения уже существует в памяти (например, после свёртывания), но Activity была уничтожена системой для экономии ресурсов. Application.onCreate уже выполнен, классы загружены, но UI создаётся заново. По данным Google, 2024, Warm Start занимает от 200 до 800 мс и составляет около 40% всех запусков на устройствах с 4 ГБ ОЗУ.
Главное
Warm Start (тёплый запуск) — это состояние между Cold Start и Hot Start: процесс приложения существует в памяти (иногда в фоновом кэше Linux), но Activity неактивна и будет создана заново. Система Android при нехватке оперативной памяти может выгрузить Activity из стека, оставив процесс живым. Когда пользователь возвращается в приложение, запускается Warm Start: создаётся новый экземпляр Activity, выполняются lifecycle-методы onCreate → onStart → onResume, но Application.onCreate и загрузка классов пропускаются.
Система 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 также скрывает этот эффект.
Понимание разницы между тремя типами запуска необходимо для выбора правильной стратегии профилирования и оптимизации. Каждый тип имеет свою длительность, свои ботлнеки и свои инструменты измерения.
| Критерий | Cold Start | Warm Start | Hot 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), которая может быть дорогой.
Система проверяет, есть ли у приложения theme для стартового окна. Если тема не установлена, отображается белый (или чёрный, в зависимости от системы) экран. Если тема установлена, отображается background из темы. Эта фаза занимает 10–30 мс, но визуально ощущается, если тема не совпадает с реальным UI приложения. Используйте Theme.Material3.DayNight с кастомным windowBackground, цвет которого совпадает с фоном первого экрана — это создаёт эффект мгновенной загрузки.
Система вызывает onCreate с передачей Bundle savedInstanceState, который был сохранён в onSaveInstanceState перед уничтожением Activity. Если приложение правильно сохранило состояние (текст полей, позицию скролла, данные ViewModel), восстановление происходит быстро. Если нет — Activity начинается с чистого листа и пользователь видит лоадер, пока данные подгружаются. Ключевой момент: ViewModel-объекты переживают Warm Start только в том случае, если процесс не был уничтожен — при Warm Start ViewModel сохраняется в памяти.
После onCreate выполняется onStart → onResume, и система вызывает первую отрисовку. TTFD (Time To First Draw) для Warm Start должен быть менее 300 мс на среднем устройстве. Если первый экран содержит сложный RecyclerView с тяжёлыми View или загружает изображения по сети, TTFD может превысить порог. Используйте Placeholder и Shimmer для плавной загрузки контента после первого кадра.
Измерение Warm Start сложнее Cold Start, так как нужно симулировать состояние "процесс жив, Activity уничтожена". Стандартная команда ADB с флагом -S не подходит — она убивает процесс. Для Warm Start используйте другие подходы.
Сначала запустите приложение через adb shell monkey или tap на иконку, затем сверните его (adb shell input keyevent 3 keyevent HOME). Подождите 5–10 секунд, чтобы система могла выгрузить Activity, и запустите adb shell am start -W (без -S). Команда вернёт время старта, которое будет короче Cold Start. Для воспроизводимости используйте скрипт: запустить → подождать → home → подождать → запустить.
# Симуляция Warm Start через ADB
$ adb shell am start -W \
com.example.app/.MainActivity
# Вывод (Warm Start):
# ThisTime: 412 ms
# TotalTime: 412 ms
# WaitTime: 423 ms
Библиотека androidx.benchmark.macro поддерживает измерение Warm Start. Для этого в тесте установите startupMode = StartupMode.WARM — библиотека запустит приложение, свернёт его, подождёт (configurable delay), а затем измерит повторный запуск. Macrobenchmark делает 10–20 прогонов и вычисляет процентили. В CI/CD можно настроить порог: если P50 Warm Start превышает 600 мс — тест падает. Это позволяет отслеживать регрессии при каждом коммите.
Firebase автоматически различает Cold и Warm Start на основе времени с момента предыдущего закрытия приложения. Если приложение было открыто в течение последних 30 минут, Firebase классифицирует запуск как Warm. В консоли Firebase вы увидите отдельные графики для каждого типа старта, что позволяет оценить эффективность оптимизаций. Например, после внедрения сохранения состояния в ViewModel можно увидеть снижение Warm Start на 30%.
Оптимизация 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 или базе данных.
Раздутие 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 мс.
// 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 от плохого. Пользователь ожидает вернуться в приложение и увидеть то же самое, что оставил — включая позицию скролла, текст в полях, выбранные вкладки.
Система вызывает onSaveInstanceState при уничтожении Activity, но ДО того, как процесс может быть убит. В Bundle сохраняются только простые данные (String, Int, Parcelable, Serializable). Для сложных данных используйте SavedStateHandle в ViewModel — он автоматически сохраняет и восстанавливает поля при Warm Start. В отличие от onSaveInstanceState, SavedStateHandle работает даже если процесс пережил Warm Start (ViewModel не уничтожается). Пример: для текста в EditText используйте SavedStateHandle.getLiveData("text") — текст сохранится и восстановится автоматически.
Если при Warm Start процесс не был убит, ViewModel остаётся в памяти и onCleared не вызывается. Это значит, что все данные, загруженные в предыдущей сессии, доступны мгновенно. Однако если процесс был убит (устройство в deep sleep более 30 минут), ViewModel уничтожается и создаётся заново с SavedStateHandle. Для корректной работы ViewModel при Warm Start используйте SavedStateHandle с полями, которые нужно восстановить при любом сценарии. Разница: ViewModel с @HiltViewModel поддерживает SavedStateHandle автоматически.
| Механизм | Процесс жив | Процесс убит |
|---|---|---|
| ViewModel | Данные в памяти | Уничтожена, создаётся заново |
| SavedStateHandle | Данные в памяти | Восстанавливаются из Bundle |
| onSaveInstanceState | Вызывается при выгрузке Activity | Не вызывается |
| Room DB | Кэш доступен | Кэш доступен (диск) |
Одна из самых частых проблем Warm Start — потеря позиции скролла. Пользователь пролистывал ленту на 50-й элемент, свернул приложение, вернулся — и видит начало списка. Решение: сохраняйте layoutManager.onSaveInstanceState (сохраняет позицию и offset первого видимого элемента) и восстанавливайте его в onRestoreInstanceState. Также можно сохранять последнюю видимую позицию в SharedPreferences с ключом по дате/времени, чтобы при Warm Start быстро восстановить позицию.
// Сохранение позиции скролла 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: использование SavedStateHandle в ViewModel и асинхронное восстановление сложных данных после старта.
SavedStateHandle автоматически сохраняет поля в Bundle и восстанавливает их при Warm Start. Поле профиля пользователя (String, JSON) будет восстановлено без лишних запросов к серверу. Если процесс был убит, SavedStateHandle загрузит последнее сохранённое состояние из Bundle.
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 для раздутия тяжёлых элементов в фоне. Пока макет раздувается, покажите placeholder с shimmer-эффектом. Это особенно важно для Warm Start, где каждая миллисекунда на счету. AsyncLayoutInflater работает на фоновом потоке и передаёт готовый View в callback на главном потоке.
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 с нуля. Это случается на устройствах с 2–3 ГБ ОЗУ при одновременной работе нескольких приложений. Фактически Warm Start гарантирован только в течение 10–20 минут после сворачивания на устройствах среднего класса.
Да, если процесс не был убит, ViewModel сохраняется в памяти и onCleared не вызывается. Это ключевое преимущество Warm Start: все данные, загруженные сетевые запросы, кэш в ViewModel — доступны мгновенно. Если процесс был убит, ViewModel создаётся заново через ViewModelProvider.Factory или @HiltViewModel, и SavedStateHandle восстанавливает сохранённые поля.
Теоретически Warm Start всегда быстрее Cold Start, но на практике есть сценарии, когда разница минимальна: если Application.onCreate был лёгким (50 мс), а Activity.onCreate — тяжёлым (800 мс), то Warm Start (800 мс) почти равен Cold Start (850 мс). В этом случае оптимизировать нужно не Application, а Activity.onCreate — именно он становится ботлнеком для Warm Start.
SplashScreen API на Android 12+ показывает системный сплэш (иконка на цветном фоне) сразу при старте — и для Cold, и для Warm Start. Для Warm Start сплэш отображается всего 100–300 мс, после чего его сменяет UI приложения. SplashScreen не ускоряет сам запуск, но маскирует время создания Activity, улучшая восприятие.
Да, потому что 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% всех запусков, и его оптимизация становится приоритетом.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также