App cold start time is the wait from a tap on the icon until the app draws its first screen, when the system has to create the process. On Android, that first frame is time to initial display (TTID). Time to full display (TTFD) continues until the app reports that the screen is usable, including content loaded from disk or the network. App launch time is Apple's name for the wait from the tap until the first screen is drawn.
Cold start performance is that measurement on a launch that creates the process. Android treats it as the startup state with the most work. Apple uses cold launch for a start just after boot, or after the app has been pushed out of memory. In Australia, a product team with an iOS or Android app can read the figures in Xcode and the Play Console.
Cold, warm and hot starts
On Android, a cold start creates the process for this launch. It happens on the first open after the device boots, and again after the system has killed the app. A warm start repeats part of that work, with more overhead than a hot start. Google describes two warm cases. In one, the person leaves and comes back while the process is still running, and the activity is created again in onCreate(). In the other, the system has evicted the app, and the process and the activity both restart, using the saved instance state passed into onCreate(). A hot start brings the activity to the foreground. If the activities are still in memory, the app can avoid repeating object setup, layout inflation, and rendering. If memory was trimmed, those objects are created again. A blank window stays up until the activity draws.
Apple separates launch from resume, then treats cold and warm as a range inside launch. A launch means the process has to start. A resume means a process is already alive, including a suspended one, and it is usually faster. Opening the app straight after leaving it is often a resume. In Apple's profiling notes, a launch after a force quit is a warm launch. A launch just after boot, or after another app has pushed yours out of memory, is a cold launch, and frameworks the app needs may have to start and be read from disk. Cold and warm launches vary with what else the device has been doing, so a single timing on one handset is only one point on that range.
Where the numbers come from
Google's app startup time page defines the three states. Android vitals alerts you in the Play Console when a startup time is excessive. The threshold uses TTID, the first frame:
- Cold startup of 5 seconds or longer.
- Warm startup of 2 seconds or longer.
- Hot startup of 1.5 seconds or longer.
Overview of measuring app performance states a faster target: a cold start under 500 milliseconds, a warm start under 200 milliseconds, and a hot start under 150 milliseconds, with the 95th and 99th percentiles kept close to the median.
TTID is reported by the framework. Logcat shows it as a Displayed line from ActivityManager. TTFD needs a call to reportFullyDrawn after the UI and its data are ready. If the app never calls it, no TTFD is reported. If the call happens before the first frame, the system reports the TTID value for both.
Jetpack Macrobenchmark's StartupTimingMetric records time to initial display and time to full display. With StartupMode.COLD, the Macrobenchmark run kills the process before the measured launch. Run it on a physical device. Google's baseline profile benchmarking guide says emulator results are likely wrong, because the emulator shares the host machine.
On iOS, Apple's Reducing your app's launch time describes the Launch Time pane in the Xcode Organizer. The pane shows milliseconds from the tap until the first screen is drawn, after the static launch screen. Filters cover device, the 50th percentile (typical), and the 90th percentile (longest), and a release can be compared with the one before it. Drawing the launch screen does not end the timer. Work after the first frame, such as replacing a spinner with the real content, sits outside the metric, while the person is still waiting for the content.
MetricKit delivers histograms for the previous day: time to first draw, and application resume time. Apple marks MXAppLaunchMetric deprecated, and the replacements include TimeToFirstDrawMetric and ApplicationResumeTimeMetric. The App Launch template in Instruments records the methods that ran during launch and when threads were blocked. On iOS, the watchdog terminates an app that takes too long to launch.
What stretches the first screen
On Android, startup slows down when Application.onCreate does heavy work, when global singletons and disk reads run during startup, or when a content provider exists only to initialise other components. For those providers, Google points to the App Startup library. The general fix is lazy initialisation: create an object the first time something needs it. On iOS, dyld loads the executable and every dynamic library before the app's code runs, and each extra third-party framework adds to that load. Static initialisers run before main(). Apple lists C++ static constructors, Objective-C +load methods, and functions marked with a constructor attribute. application(_:didFinishLaunchingWithOptions:) then runs on the main thread, and launch waits until it returns. Keep that method to what the first screen needs.
Apple counts the views drawn on the first frame. Android's TTID counts that frame as well, so a deep hierarchy, bitmap decoding, or a heavy first composition adds to the number. A screen that waits on the network before drawing adds each round trip. When several requests run one after another, the start waits for the series. Apple's article says to show content you already have, and to refresh it off the main thread, when stale content is acceptable on that screen. Android's matching note is to draw with placeholder data, then fill in images and other resources.
The same deferral covers a session check. If the check has to finish before anything is drawn, its wait is part of every cold start. Draw the last screen from local state, then check the token. If the token has expired, ask for a sign-in once that screen is up. When the app is restoring a screen, prepare that screen. When there is no saved state, prepare only the default first screen.
On Android 11 and earlier, a dedicated splash activity, or a theme that sets windowDisablePreview, can add time. From Android 12, a custom splash screen has to use the SplashScreen API. On iOS, the static launch screen sits inside the measured interval. Delaying the first real frame, so a branded screen stays up, lengthens that Organizer time.
Android startup profiles are the startup subset of baseline profiles. The build uses them so startup classes sit in the primary DEX file. Google's startup profiles documentation says app startup is usually between 15% and 30% faster than with baseline profiles alone. R8, with minify enabled on the release build, removes unused code so that startup code is more likely to fit in the first DEX file. On iOS, fewer embedded third-party frameworks shorten the work dyld does at launch. From Xcode 15, mergeable libraries can give release builds a launch time similar to static linking, with debug builds still dynamically linked.
What to check on the next release
Read the field numbers before changing code. In the Play Console, open the startup vitals. In Xcode, open Launch Time and note the 50th and 90th percentiles on the devices you support.
Reproduce a cold start on a physical device. On Android, use Macrobenchmark with StartupMode.COLD, or read the Displayed line. On iOS, use the App Launch template, once after a reboot and once after a force quit.
Leave SDK setup that can wait, a network refresh, and a session round trip until after the first screen is drawn. Show the cached or placeholder screen, then refresh. Compare the new build with the 90th percentile you wrote down, on the same device class.
A build of the iOS or Android app is Mobile App Development. Watching startup after release, and fixing a version that gets slower, is App Maintenance & Support.