What Cold Start Actually Means
Cold start is the time between the user tapping your app icon and seeing the first meaningful frame. It includes process creation, Application class initialization, Activity creation, layout inflation, and the first draw pass. Every ContentProvider, every eager singleton, every synchronous SDK initialization adds to this number.
On a mid-range device — the kind most of your users actually have — a poorly optimized cold start can easily exceed 3 seconds. That is long enough for users to question whether the app is broken.
Step 1: Measure Before You Optimize
The first mistake teams make is optimizing without a baseline. I use three measurement approaches:
- ADB shell timing —
adb shell am start -S -W com.package/.MainActivitygives you TotalTime, which represents the system-measured cold start duration - Macrobenchmark — Automated, repeatable benchmarks that run on real devices in CI, capturing p50 and p90 startup times across runs
- Firebase Performance — Real-world data from actual users on actual devices, segmented by device tier, OS version, and network condition
Step 2: Audit Your Application Class
The Application class is the single biggest contributor to cold start time. Every line of code in onCreate() runs before the user sees anything. I categorize initializations into three buckets:
- Critical (keep) — Crash reporting, dependency injection graph creation, security checks
- Deferrable (background) — Analytics SDKs, ad networks, feature flags, remote config
- Lazy (on-demand) — Image loading libraries, database migrations, ML model loading
Move everything in the second bucket to a background coroutine launched after the first frame renders. Move everything in the third bucket to lazy initialization triggered by first use.
Step 3: App Startup Library
Android's App Startup library replaces the ContentProvider-based initialization pattern. Instead of each library declaring its own ContentProvider (each adding ~2ms to startup), you declare a single provider and order your initializations explicitly. This alone saved 80-120ms across our apps.
Step 4: Baseline Profiles
Baseline Profiles are the highest-impact, lowest-effort optimization available. By generating a profile of your app's critical code paths, you instruct the ART runtime to pre-compile those methods during installation. The result: methods that would normally be interpreted on first run are already compiled to native code.
In our testing, Baseline Profiles improved cold-start time by 15-20% with zero code changes. The only effort is writing a Macrobenchmark test that exercises your critical user journeys.
Step 5: Layout Optimization
The first layout inflation is part of cold start. Deep view hierarchies, nested ConstraintLayouts, and complex data binding expressions all contribute. I follow these rules:
- Keep the initial layout flat — maximum 3-4 levels of nesting
- Use
ViewStubfor content that is not immediately visible - Defer RecyclerView population to after the first frame — show a skeleton or shimmer effect instead
- If using Jetpack Compose, avoid heavy computation in the initial composition — move data loading to LaunchedEffect
The Results
Applying these techniques systematically across 15+ apps yielded a consistent 25% reduction in cold-start time. The median startup improved from 1.8s to 1.35s. On low-end devices, the improvement was even more dramatic — from 3.2s to 2.1s.