Optimizing Mobile App Cold Start Time
We often see apps lose users due to slow launch times. A cold start is a launch from scratch where the process does not exist in memory. The OS creates a process, loads the binary, initializes the runtime, runs the Application/AppDelegate, and renders the first screen. On Android, this is the path from the icon to Activity.onResume(); on iOS, to the first frame. Our experience shows that slowdowns almost never have a single cause — it's accumulated technical debt: synchronous SDK initializations, heavy operations on the main thread, and a bloated splash screen.
We offer a comprehensive audit and optimization of cold start turnkey. In 1–3 weeks, we identify bottlenecks, implement deferred loading, configure Baseline Profiles, and reduce launch time to target values. Request an audit — we will evaluate your project for free.
In this article, we will break down typical problems on Android and iOS, show diagnostic tools, and propose proven solutions. If you want immediate consultation, feel free to contact us.
Why is cold start critical for user experience?
Research by Google shows: 53% of users close an app if it takes longer than 3 seconds to launch. For games and social networks, the threshold is even lower — 2 seconds. Cold start is the first impression, and it must be fast.
Where time is lost: Android
Android Vitals in Play Console shows the "Startup time" metric — the percentage of sessions with cold start > 5 seconds. But this is aggregate. For diagnostics, you need Android Studio Profiler → App Startup or Perfetto.
A typical picture during an audit: Application.onCreate() takes 800–1200 ms on a mid-range device, and most of that is synchronous initialization of Firebase, Amplitude, AppsFlyer, OneSignal, and three other SDKs. Each internally does SharedPreferences.read, creates a HandlerThread, and registers a BroadcastReceiver.
Solution: App Startup Library (androidx.startup) with an explicit dependency graph for initializations. SDKs needed immediately (Crashlytics) — synchronous. Analytics, push — via ContentProvider lazy initialization or in a background thread with a 2–3 second delay after the first render.
A second source of loss is the Dagger/Hilt dependency graph at startup. If @Singleton components are heavy (Room database, Retrofit instances) and are created all at once, it shows up as a spike in Profiler right after onCreate. Solution: @Lazy<T> for components not needed on the first screen, and backgroundScope.launch for repository initialization.
Baseline Profiles (Jetpack) — pre-compilation of hot code paths into AOT before the JIT sees them. ProfileInstaller + BaselineProfileRule in tests can reduce cold start by 30–40% on first launches after installation/update. This is not magic — it’s explicitly marking "these classes must be compiled in advance."
How to properly measure cold start time?
Measure on real devices with typical load. The emulator does not reflect real I/O and JIT performance. Use system logs or specialized tools.
| Tool | Platform | What it shows |
|---|---|---|
| Android Vitals | Android | Aggregated session data |
| Perfetto | Android | Kernel and app-level events |
| Instruments Time Profiler | iOS | Time until first frame |
| os_signpost + DYLD_PRINT_STATISTICS | iOS | Pre-main phase |
Where time is lost: iOS
os_signpost + Instruments Time Profiler is the only correct way to see the real picture. Xcode shows time from tap to applicationDidFinishLaunching, and separately time to the first meaningful render.
The main culprits on iOS: +load methods in Objective-C classes and C++ static constructors. They execute before main(), and their time is not visible in the regular Profiler without special instrumentation. DYLD_PRINT_STATISTICS in environment variables will show the real pre-main phase time.
Swift initialization is faster than Obj-C, but there are pitfalls: a heavy init in the @UIApplicationMain class, singletons via static let shared = ... that are created in application(_:didFinishLaunchingWithOptions:) in a chain.
URLSession, CoreData stack, Keychain — all of this should be initialized lazily or in the background. CoreData NSPersistentContainer.loadPersistentStores is asynchronous by default, but developers often wrap it in a semaphore, making it a synchronous call on the main thread.
Metrics and target values
| Device type | Good | Acceptable | Poor |
|---|---|---|---|
| Android high-end | < 1.0 s | 1.0–2.0 s | > 2.0 s |
| Android mid-range | < 2.0 s | 2.0–4.0 s | > 4.0 s |
| iPhone (last 3 generations) | < 0.8 s | 0.8–1.5 s | > 1.5 s |
| iPhone (5+ years old) | < 1.5 s | 1.5–3.0 s | > 3.0 s |
Metrics are measured on real devices, not emulators.
Flutter and React Native
In Flutter, cold start is bottlenecked by Dart VM and engine initialization. FlutterActivity vs FlutterFragmentActivity — a difference of 50–100 ms. Pre-initialization of the engine via FlutterEngineCache + FlutterEngineGroup allows reusing the engine between launches. Splash screen via flutter_native_splash correctly synchronized with the native launch screen.
In React Native, the problem is JS bundle load time. Hermes engine (compilation to bytecode) reduces parse-time by 2–3x compared to JSC. RAM Bundles and inline requires allow loading only the code needed for the first screen.
Typical mistakes we find
- Initializing all SDKs in Application.onCreate() without considering priorities.
- Synchronous database load (Room/CoreData) on the main thread.
- Missing Baseline Profiles or incorrect configuration.
- Using an emulator for performance measurements.
What’s included in the work
- Audit — profiling on 3–5 device types, identifying top-5 bottlenecks.
- Report — a detailed document with diagrams and recommendations.
- Implementation — deferred initialization, dependency graph optimization, Baseline Profiles setup.
- Testing — repeated measurements on the same devices, stress tests.
- Documentation — description of all changes and maintenance instructions.
- Guarantee — support for one month after project delivery.
Optimization process
First, we measure — without baseline metrics it's unclear what to optimize. A Profiler session on 3–5 real device types. Then, analysis of hot paths, prioritization by contribution to total time. Implementation of changes iteratively with measurement after each change. Final before/after comparison on the same set of devices.
Project timeline — one to three weeks depending on architecture complexity and number of platforms.
Our team has 10+ years of experience in mobile development and has successfully optimized over 50 apps. We guarantee a transparent report for each stage.
Contact us for an audit of your app — we will evaluate the project and propose an optimization plan.







