Optimizing Mobile App Warm Start Time
Warm start occurs when the app was previously launched and process is alive in memory but Activity/ViewController is recreated. On Android this is typical after swiping from Recent Apps and reopening, or after system kills Activity but leaves process. On iOS — returning to app after prolonged background stay when ViewController was unloaded.
Warm start is slower than hot start (when process and Activity are alive) but faster than cold start — JVM/Dart VM/JS runtime already launched, Application code already executed. Problem is recovery during warm start is often done incorrectly.
Android: SavedInstanceState and ViewModel
Main warm start trap on Android — incorrect SavedInstanceState handling. When Activity is destroyed, system calls onSaveInstanceState, developer saves data, Activity recreates with savedInstanceState != null. Good — until Bundle contains big objects.
Bundle isn't designed for serializing large data. 500KB images in Bitmap, serialized list of 200 objects — TransactionTooLargeException at best, silent crash at worst. Rule: in Bundle — only ID, minimal state. Data — in ViewModel that survives Activity recreation.
ViewModel with SavedStateHandle — correct approach: SavedStateHandle stores only ID/primitives in Bundle, full data stored in ViewModel.stateFlow, restored from repository by ID when needed.
Heavy operations in onCreate during warm start — classic mistake. Developer writes code for cold start, forgetting that during warm start onCreate is called again. Room initialization, Retrofit client creation, WorkManager start — none should repeat per onCreate. Dagger/Hilt @Singleton solves it for infrastructure components, but initialization logic needs monitoring.
iOS: Lifecycle and State Restoration
On iOS warm start occurs returning when ViewController was unloaded due to didReceiveMemoryWarning. viewDidLoad is called again, viewWillAppear too. Problem — if all screen init logic is in viewDidLoad, it executes again: makes extra network requests, recreates UI, loses scroll position.
UIKit State Restoration API (encodeRestorableState, decodeRestorableState) — correct mechanism, but rarely used due to complexity. More common is manual approach: save state in UserDefaults or via Codable to file.
SwiftUI handles this better via @StateObject and @AppStorage — state automatically survives View recreation. But using UIKit-hosting (UIHostingController) requires ensuring @StateObject isn't recreated per wrapping.
Main loss source on iOS during warm start — repeated network requests for data already loaded before unload. Proper caching layer in repository (NSCache for in-memory, CoreData/Realm for persistence) instantly shows cached data with background update.
Practical Case
E-commerce app with product catalog. Warm start on mid-range Android took 1.8 seconds. Profiler showed: 900ms — Retrofit/OkHttp client recreation in Fragment.onCreateView, 400ms — synchronous Room query to load categories, 500ms — inflating complex RecyclerView layout.
Fixes: Retrofit in @Singleton via Hilt, Room query moved to ViewModel.init with viewModelScope.launch, categories cached in memory with 5-minute TTL, layout simplified with ViewBinding precompile. Result: warm start 0.4 seconds.
Metrics and Tools
Android: adb shell am start -W package/activity — shows TotalTime for warm start. For detailed analysis — Perfetto with ActivityThread.handleStartActivity section. Firebase Performance Monitoring auto-tracks startup traces in production.
iOS: Instruments → Time Profiler with App Launch template. MetricKit in iOS 13+ collects MXAppLaunchMetric with cold/warm/resume breakdown.
Optimization timeline: one to two weeks depending on screen count and architecture complexity.







