Оптимізація часу запуску мобільної програми (Warm Start)
Warm start — програма вже була запущена, процес живий в пам'яті, але Activity/ViewController пересоздається. На Android це типова ситуація після свайпу з Recent Apps та повторного відкриття, або після того як система вбила Activity, залишивши процес. На iOS — повернення в програму після тривалого перебування в фоні, коли ViewController було вивантажено з пам'яті.
Warm start повільніший за hot start (коли процес та Activity живі), але швидший за cold start — JVM/Dart VM/JS runtime уже запущені, код Application уже виконаний. Проблема в тому, що відновлення стану при warm start часто робиться неправильно.
Android: SavedInstanceState та ViewModel
Головна ловушка warm start на Android — неправильна обробка SavedInstanceState. При знищенні Activity система викликає onSaveInstanceState, розробник зберігає дані, Activity пересоздається з savedInstanceState != null. Все добре — поки в Bundle не попадають великі об'єкти.
Bundle не призначений для серіалізації великих даних. 500KB зображень у Bitmap, серіалізований список з 200 об'єктів — це TransactionTooLargeException у кращому випадку, у гіршому — тихий крах. Правило: в Bundle — лише ID, мінімальний стан. Дані — у ViewModel, яка переживає пересоздання Activity.
ViewModel з SavedStateHandle — правильний підхід: SavedStateHandle зберігає лише ID/примітиви в Bundle, повні дані зберігаються в ViewModel.stateFlow, відновлюються з репозиторію за ID при необхідності.
Важкі операції в onCreate при warm start — класична помилка. Розробник пише код для cold start, забуваючи що при warm start onCreate викликається знову. Ініціалізація Room, створення Retrofit-клієнта, запуск WorkManager — все це не повинно повторюватися при кожному onCreate. Dagger/Hilt @Singleton розв'язує це для інфраструктурних компонентів, але за логікою ініціалізації потрібно стежити.
iOS: життєвий цикл та State Restoration
На iOS warm start відбувається при поверненні в програму після того, як ViewController було вивантажено через didReceiveMemoryWarning. viewDidLoad викликається знову, viewWillAppear теж. Проблема — якщо вся логіка ініціалізації екрану в viewDidLoad, вона виконується повторно: робить зайві мережеві запити, пересоздає UI, втрачає позицію скролу.
UIKit State Restoration API (encodeRestorableState, decodeRestorableState) — правильний механізм, але використовується рідко через складність. Частіше зустрічається ручний підхід: збереження стану в UserDefaults або через Codable у файл.
SwiftUI краще справляється з цією проблемою через @StateObject та @AppStorage — стан автоматично переживає пересоздання View. Але при використанні UIKit-хостингу (UIHostingController) потрібно стежити за тим, щоб не пересоздавати @StateObject при кожному обгортанні.
Головна статья втрат на iOS при warm start — повторні мережеві запити даних, які вже були завантажені до вивантаження. Правильний шар кешування в репозиторії (NSCache для in-memory, CoreData/Realm для persistence) дозволяє миттєво показати дані з кешу та оновити в фоні.
Практичний кейс
Програма e-commerce з каталогом товарів. Warm start на mid-range Android займав 1.8 секунди. Profiler показав: 900ms — пересоздання Retrofit/OkHttp клієнтів у Fragment.onCreateView, 400ms — синхронний запит до Room для завантаження категорій, 500ms — inflate складного RecyclerView layout.
Виправлення: Retrofit в @Singleton через Hilt, Room-запит переенесен у ViewModel.init з viewModelScope.launch, категорії закешовані в memory з TTL 5 хвилин, layout спрощений з ViewBinding precompile. Результат: warm start 0.4 секунди.
Метрики та інструменти
Android: adb shell am start -W package/activity — показує TotalTime для warm start. Для детального аналізу — Perfetto з секцією ActivityThread.handleStartActivity. Firebase Performance Monitoring автоматично трекає startup traces у продакшені.
iOS: Instruments → Time Profiler з App Launch шаблоном. MetricKit у iOS 13+ збирає MXAppLaunchMetric з розбивкою на cold/warm/resume.
Термін оптимізації: одна-два тижні залежно від кількості екранів та складності архітектури.







