I’ve gathered the Android app startup optimization approaches I knew of when writing this article. Use them as a checklist: see what others have tried, then look for gaps in your own app. The details depend heavily on your business, so I won’t explain every implementation here. Once you find an approach that fits, follow up on that particular method.
I’ve also included some startup work done by system manufacturers, limited to the parts I know about. There are proprietary tricks I can’t cover here. Knowing what happens on the system side may still help, including when discussing whitelisting or SDK integration with a manufacturer.
Version note — reviewed October 13, 2026: This article was published in 2019. The original examples and categories remain, with updated startup definitions, measurement guidance, and splash-screen behavior. OEM whitelists, prediction, and restart strategies are historical leads rather than capabilities guaranteed on every device. Process-pool details use tagged Android 10 / 16 source.
First define the endpoint: the first frame, primary content, and interactivity are different milestones. Repeated Macrobenchmark journeys and Perfetto traces help locate main-thread, RenderThread, Binder, I/O, and GC costs; field monitoring should separate devices and versions. Baseline Profiles guide ART compilation, whereas Startup Profiles optimize DEX layout during the build.
App Startup Overview
General App Startup Flow
Launching from an icon can involve system scheduling, process and component initialization, UI construction, and content loading. This older diagram shows only some of those stages:

- SystemServer coordinates process, Activity, and window management. It requests a normal App process through Zygote or an eligible USAP; SystemServer does not directly fork the App itself.
- The App process handles binding, Provider initialization,
Application.onCreate(), and Activity work after its main Looper exists. Not every launch initializes all four component types; Services and Receivers depend on the entrypoint and workload.
The diagram places Application.onCreate outside its MainThread box. That does not mean it runs on another thread or that the main thread is created afterward. Network content and interactivity may still be pending after the first frame.
Cold Start vs. Hot Start
The original two-way classification also needs warm starts. For common process/Activity states:
- Cold: The App process needs to start, with application and UI initialization.
- Warm: Typically, the process survives but the target Activity needs recreation, including
onCreate(). - Hot: An existing Activity and its UI state can be brought forward, though content restoration, layout, or drawing may still be needed.
Back and Home are not reliable switches between these categories. Activity behavior varies, background processes can be reclaimed, and a recent-task card does not prove that a process survives. Record process and Activity recreation, and use the measurement tool’s definitions. See startup states.
Measuring Startup Speed
Different teams have their own ways of measuring startup. What has always bothered me is deciding exactly when it ends. Complex screens need an observable completion condition, rather than whichever number looks smallest.
- TTID: The destination screen’s first displayed frame. It may still contain placeholders or loading UI.
- TTFD: The App signals primary-content readiness through
reportFullyDrawn()or an appropriate ReportDrawn API. Its meaning depends on the App’s reporting condition; it does not automatically validate every interaction. - Interactivity or business completion: A usable list or completed login needs an explicit scenario and assertion. OCR can identify expected content, but visible text does not prove an image finished loading or a control is usable.
Macrobenchmark StartupTimingMetric reports timeToInitialDisplayMs and timeToFullDisplayMs. The latter ends with the relevant rendered frame containing or following the fully-drawn signal. Missing reporting is not a zero duration. Fix the release build, startup mode, compilation/Profile state, account and data state; repeat on physical devices, compare distributions, and keep traces. See Macrobenchmark setup.
adb shell am start -W remains useful for quick diagnosis, but its fields vary by version. Older output commonly has ThisTime/TotalTime/WaitTime; AOSP Android 16 prints LaunchState, TotalTime, and WaitTime, without ThisTime. WaitTime is neither content readiness nor pure App CPU time. A single command or screen recording does not replace a release benchmark.
The original Mobile Taobao case used automated, repeatable, CI-integrated measurement with OCR. Its OCR-service accuracy figure does not establish the accuracy of detecting this App’s startup endpoint.
App-Level Optimizations
Startup Window Optimization
A system StartingWindow, Android 12 SplashScreen, and an App’s own SplashActivity are different things. A splash improves waiting feedback; it does not remove initialization work.
- For legacy preview windows, avoid unnecessary
android:windowDisablePreview="true"settings that leave a blank wait. Keep the launch theme visually consistent with the destination. - Android 12 (API 31) introduces the standard system SplashScreen for cold and warm launches. Follow the migration guide; old background-image and preview-disable tricks are not universal modern solutions. An additional SplashActivity may produce two consecutive splashes.
- Route directly to the destination where appropriate. Login, deep links, and advertising have their own constraints; measure them separately and preserve state restoration.
The original observations about WeChat, Zhihu, and TikTok did not provide reproducible versions or whitelist evidence. They cannot establish those apps’ current configuration or how often they start cold.
Thread Optimization
Control runnable work and dependency waits. Excess runnable threads can increase main-thread scheduling delay; Sleep may instead reflect locks, condition variables, Binder, or ordinary idle time.
- Bound thread-pool concurrency and backlog according to task type. A pool alone does not prevent overload.
- Follow locks to their owners. Moving a dependency to a worker and immediately waiting on the main thread can preserve the original blockage.
- Distinguish main-thread-only initialization from work that can run elsewhere. mmkernel and Alpha are historical architecture references, not mandatory standard components.
System Scheduling Optimization
Reduce unnecessary work on the startup critical path:
- Consolidate repeated synchronous Binder queries and investigate AMS/ATMS or WMS lock waits. Binder calls are not synonymous with all system calls, nor does every call compete for the same lock.
- Defer extra processes not needed for the first screen. They add initialization and contention, but the cost does not simply double. Preserve required process isolation.
- Inspect Provider and Service initialization on their process’s main thread, including Providers initialized before
Application.onCreate(). Remote Provider requests and Service Binder methods can instead run on Binder pool threads. - Move suitable work off the main thread with an explicit readiness dependency. Main-thread-only essentials still need to be shortened.
The old CPU capture shows interleaved work. Colors alone do not establish saturation or identify a single App as its cause:
The SystemServer image shows monitor contention and lock-owner threads. Its 93.633 ms selection is an observation window, not the sum of every lock wait or the full launch time. Follow the owners:
GC Optimization
Reduce unnecessary startup allocations, then inspect allocation volume, GC pauses, and main-thread waits. Counting collections alone is insufficient.
- Avoid repeated large strings, serialization, and parsing content not needed for the first screen.
- Reuse objects only with clear ownership and size limits. Pools can increase retained memory or lock contention.
- Native allocation still consumes process memory and adds JNI, lifetime, and leak concerns. Moving code to Native is not a general way to eliminate memory cost.
Historical example: Alipay startup garbage-collection optimization.
IO Optimization
File access, page faults, decompression, databases, and other processes’ I/O can interact during startup. Record the file, actual bytes transferred, call count, thread, and duration; distinguish parsing CPU time from waiting. File size is not the amount read or written by an operation.
Required first-screen network requests can start asynchronously; defer nonessential requests. A blanket ban on startup networking does not fit online-content screens. Move suitable disk operations off the main thread without immediately waiting for them there.

This is the original table of observation dimensions: the 500 MB chat.db file and the 800 KB write are separate fields. Its KB/MB labels do not specify decimal versus binary units, so they cannot establish exact throughput. The durations and Buffer sizes are examples, not recommended settings.


These captures involve the system screen recorder and another App, not a controlled comparison of one process. Identify I/O waits using the selected thread state, blocking stack, and related events. Orange/red method slices are not automatically I/O, and the partial images alone do not establish low memory as the root cause.
Resource Reordering
The historical approach rearranges APK resources to improve startup access locality, making better use of page cache and readahead. See the original Alipay package-layout case.
Page cache holds file content for ordinary buffered I/O and file mappings. Readahead benefits depend on access patterns and cache state. Adjacent file offsets do not guarantee contiguous physical storage, and page size, filesystem blocks, APK compression, and mapping behavior are separate concerns.

The diagram is labeled Linux 4.0 (2015); it is a historical illustration, not the complete modern Android storage stack. Modern packaging includes split APKs, compression, alignment, and signing. Reordering belongs inside the build/signing pipeline, followed by artifact validation. A warm cache may leave little benefit; compare equivalent device and cache conditions rather than assuming fewer disk reads.
Class Reordering
ReDex InterDex can use a startup class list to improve DEX layout; see the project documentation. DEX capacity, primary-DEX constraints, and other optimizations still apply. “Put every startup class in the primary DEX without considering constraints” is not an adequate implementation model. Measure the result with an equivalent A/B journey.
Consider Startup Profiles for current builds. They improve startup-code locality during the build; Baseline Profiles supply critical paths to ART for compilation. According to the configuration guide, DEX layout optimization is available from AGP 8.1 and enabled by default from 8.3. AGP 8.1–8.2 need explicit enablement, with R8 configured as documented. The recommended tool combination is Macrobenchmark 1.2.0+ and AGP 8.2+. Confirm that the release artifact consumes the generated rules, and test launcher and deep-link entries rather than adopting another app’s improvement percentage.
Main Page Layout Optimization
Inspect actual Measure, Layout, and Draw costs—or Compose composition and layout—on the target screen:
- Reduce redundant hierarchy and repeated measurement; hierarchy depth is not the only cost.
- Use ViewStub for Views not yet needed, while checking the first-expansion delay instead of moving jank to the first tap.
- A custom View can reduce some stacking costs but brings interaction, accessibility, and drawing maintenance. It is not automatically faster than standard components.
Idle Calls
IdleHandler belongs to a MessageQueue and runs when its Looper’s queue meets idle conditions, not when a particular Handler is idle. Returning true retains it for a later idle callback; false removes it. Returning true does not create an immediate infinite loop.
The callback still executes on that Looper’s thread. A main-queue IdleHandler should not directly perform network requests, heavy I/O, or large View construction. It neither guarantees a deadline nor moves work to a worker thread. It can cheaply schedule nonessential work, but required first-screen tasks must not wait indefinitely for idle time. See IdleHandler and Android 10 MessageQueue.
Class Loading Optimization
A VerifyClass trace slice can identify verification work, but its wall duration can include scheduling waits. Every startup does not necessarily verify every method again. Inspect the ART version, compilation state, and loading path.

This dexopt / VERIFY_MODE_NONE diagram describes an old runtime model. It is not guidance for disabling modern ART verification. Since Android 7, ART combines interpretation, JIT, and AOT, with version-dependent artifacts and policy; see ART configuration. Reduce needless initialization and correctly generate, package, and validate Baseline Profiles. A packaged Profile alone does not prove that a particular device launch uses precompiled code.
App Thinning
Package size, installed storage, decoded-image memory, and startup duration are different metrics:
- Use Android Lint and IDE inspections to find unused content, then check reflection, JNI, and dynamic-resource retention requirements.
- Configure R8 and resource shrinking for release builds. Obfuscating names and removing unused content are different operations.
RGB_565is a 2-byte-per-pixel Bitmap memory configuration without alpha, not an APK file format replacing PNG or JPEG. Consider transparency, color, and actual decode behavior; choose compression and decode dimensions separately.- Renaming resources or reducing DEX count does not guarantee a faster launch. Inspect real artifacts and startup work without breaking resource lookup or adding work merely to reduce a file count.
Choosing the Right Startup Framework
Model initialization and dependencies as a Directed Acyclic Graph (DAG), then choose stages, threads, and concurrency bounds. The graph expresses ordering; it does not require multiple processes or automatically provide parallelism and congestion control. Define success and failure for required dependencies. Submitting a worker task is not the same as completing initialization.
These three images retain the original Taobao stages: core libraries, screen dependencies, optional preloading, and on-demand initialization. m-a, m-f-a, m-b-f, and m-idle are case-specific labels, not Android lifecycle states. The table’s guaranteed-execution column does not establish a guarantee for arbitrary IdleHandlers.



Jetpack App Startup can express Initializer.dependencies() and consolidate automatic initialization under one Provider. It synchronously calls create() in dependency order; it is not an automatic parallel scheduler. Default eager initialization usually runs on the main thread before Application.onCreate(). For manual/lazy initialization, inspect the merged manifest and remaining initializer dependencies to avoid early or missing initialization.
Historical case: Taobao full-link optimization.
Startup Network Optimization
These categories come from the historical Taobao case. Library names, threads, and durations describe that workload rather than default behavior in every App.
Problems and Optimization Points
- Sending: library setup or
bindServicecan delay requests; image tasks may wait in bounded queues. - Transfer: oversized or unnecessary resources compete with first-screen data.
- Parsing: JSON, decoding, and callbacks occupy finite worker capacity.
- Display: results may be available while main-thread queues or heavy callbacks still delay presentation.
The original 3-second parsing and 1-second/high-end versus 3-second/low-end delays lack reproducible conditions here. They are historical case descriptions, not device-tier thresholds.
Optimization Strategies
- Deduplicate requests and defer nonessential data. Deduplication is not indiscriminately merging every network connection.
- Bound download, parsing, and image-decode concurrency to avoid shifting the bottleneck to CPU, memory, or callback queues.
- Parse on suitable workers, apply only necessary results on the main thread, and handle cancellation and stale callbacks.
- Refresh intervals at 60 / 90 / 120 Hz are about 16.67 / 11.11 / 8.33 ms. One callback cannot consume the whole interval; staying under 16 ms is no guarantee against jank. Use the actual deadline and all main-thread work.
Preloading
When upcoming navigation is predictable, prepare data asynchronously and reuse it when the screen is ready. Handle cancellation, errors, account changes, expiration, and memory limits. Without an opportunity to run earlier, relocating the work need not reduce end-to-end waiting.
The original PreLoader project remains a historical implementation reference. Check maintenance and compatibility before integrating it.
Keep-Alive
Keeping a process can avoid some work on the next launch, but a destroyed Activity may still require a warm start. Process survival is not synonymous with a hot start and must not substitute for cold-start optimization.
The aim here is useful functionality and responsible resource use, not mutual wake-ups or notification flooding. Release background resources and stop unnecessary services and animations.
I know this can sound idealistic from an App developer’s side. You may release resources carefully and still have the process removed by a manufacturer’s background policy. Improving Android’s ecosystem takes work from both sides. Background limits, user settings, and OEM policies vary by version. Whitelisting is a specific cooperation arrangement, not a standard reward for reducing memory use. The App must still recover correctly after process death and cannot assume it always receives a low-memory callback first.
Business Organization
For each startup module, identify whether it is required, what it depends on, and which thread may execute it. The original four categories remain useful:
- Required and expensive: Reduce the work first, then move allowed parts off the main thread. Define readiness for results needed by the first screen.
- Required and inexpensive: Schedule it appropriately and account for cumulative cost.
- Optional and expensive: Defer, load on demand, or cancel; avoid releasing a burst of all deferred work after the first frame.
- Optional and inexpensive: Skip it during startup and load it when needed. This does not mean deleting useful functionality.

Staged, asynchronous, and deferred loading are different techniques; moving every expensive task to a worker does not finish the optimization.
Business Optimization
- Locate large bottlenecks and repeated work first, then check other entrypoints for regressions.
- Address technical debt within the scope of demonstrated startup problems.
The original business optimization case offers a checklist, but two claims need correction:
THREAD_PRIORITY_BACKGROUND = 10is an Android thread-priority constant, not a maximum 10% CPU share. Scheduling groups, other runnable tasks, and device policy affect CPU allocation. Do not blindly lower the priority of a dependency the main thread urgently needs. See Process.- Each process in a multiprocess App has its own Application lifecycle. Gate particular SDK/business initialization by process; the system does not call
Application.onCreate()only in the main process. Secondary processes still need their own required initialization.
Other transferable ideas include deferring nonessential updates and analytics; removing unused legacy, debug, duplicate, and SDK-demo work; bounding total concurrency; and caching repeated queries with expiration and account boundaries. After moving database or I/O work to a worker, verify that the UI no longer waits synchronously for it.
Reducing Activity Navigation Hierarchy
These are historical window-transition sketches. Whether and when a starting window appears depends on launch type and system version. A tap does not guarantee immediate display, and constructing an AppWindow object alone does not complete the handoff. Use the SplashScreen lifecycle when analyzing Android 12+.
Default App Startup Window Flow
1 | StartingWindow(SystemWindow) |
Most Third-Party App Startup Flow
1 | StartingWindow(SystemWindow) |
Worse Startup Flow
1 | StartingWindow(SystemWindow) |
Even Worse: Disabled StartingWindow
1 | SplashActivity(AppWindow) |
Removing pointless intermediate Activities can shorten the path, while login, deep links, and restoration still need correct routing. The original “worse” labels describe an observed experience, not a platform ban on every SplashActivity.
Manufacturer Optimizations
The following categories retain the original system/OEM discussion. Except where tagged source is available, these are mostly observations from 2019 with unconfirmed coverage and current behavior. App acceptance must not depend on an undocumented vendor policy.
Startup Acceleration
Scheduling, performance hints, and frequency policy may help startup, subject to thermal limits, power, contention, and implementation. There is no cross-device guarantee of absolute resource preference.

The image contains CPU frequency, limits, and scheduling tracks without all values or policy settings. Dense colors do not prove that one App owns CPU, I/O, and GPU resources. Vendor SDKs need concrete device documentation; a performance request does not guarantee a big core.
PreFork
Android 10 (Q) introduced the USAP (Unspecialized App Process) pool. Processes are forked before being specialized for a particular App. This does not repeatedly recycle a process that previously ran another App.
Android 10 ZygoteProcess has a disabled default; device configuration determines enablement, and startup arguments must be eligible. Android 16 still checks policy and eligibility, with fallback to ordinary Zygote launch after a USAP communication failure. Ordinary Apps cannot require every Android 10+ device to use the pool.
The original process/memory snapshot follows verbatim, including its punctuation. It establishes those USAP entries in that capture, not a fixed pool size, fork duration, or universal memory cost.
1 | 2,348K: usap32 (pid 18731) |
Message Reordering
The original article lists this vendor direction without a public versioned implementation. Reordering must preserve lifecycle, synchronization-barrier, and dependency semantics. Posting arbitrary App messages to the front of the queue is not a general startup optimization.
Main Thread & Render Thread Acceleration
An OEM may adjust scheduling groups, CPU affinity, or performance hints during selected stages. Use traces and device policy to establish whether main/RenderThread work runs on big cores. Forced migration is not universal and can have thermal or contention costs.
Startup Prediction
A system may prewarm processes, code, or data based on usage history. The prediction policy is implementation-specific. A prewarmed process does not prove the target Activity exists or guarantee a hot launch. Separate prewarming hits and misses in measurements.
Background Keep-Alive
The system manages background processes according to importance and resource pressure, with additional OEM policies or user settings on some devices. Retention can avoid some initialization, but guarantees neither survival nor a hot next launch.
Background Restart
The original “seamless background restart” is an unversioned vendor observation, not a standard Android startup mechanism. Restarting can temporarily reclaim leaked memory without fixing the leak or repeated-crash cause. It guarantees neither preserved user state nor a hot next launch.
Memory Optimization
Memory-intensive launches may trigger reclaim, faults, and resource contention. The system can reclaim cache and, when necessary, low-priority processes; it does not always have to kill many Apps. Proactive vendor reclaim is one possible strategy whose benefit must be weighed against later restarts of reclaimed Apps.
Optimizing Startup Logic
System updates can shorten startup paths. The retained 2019 ViewRootImpl screenshot shows “Immediately start drawing when becoming visible”: removing an old new-Surface drawing delay, with a stated possible 5–10 ms first-draw delay at the time.

This is a particular system-branch change, not evidence that reducing any App’s doFrame count always improves startup. The commit-message estimate is not a current-device measurement.
OEM-Level Optimizations
The system and vendor categories above have different evidence and version boundaries; none substitutes for measuring the App on its supported devices.
References
- 都9102年了,Android 冷启动优化除了老三样还有哪些新招?
- App startup time
- 历时1年,上百万行代码!首次揭秘手淘全链路性能优化(上)
- 极客时间 : Android 高手开发课
- Facebook-Redex
- 关于 Android 异步启动框架 alpha 的思考
- 支付宝 App 构建优化解析:通过安装包重排布优化 Android 端启动性能
- Redex 官网
- Redex 初探与 Interdex:Andorid 冷启动优化
- Android性能优化笔记(一)——启动优化
Other Locations for This Article
Since blog comments aren’t convenient for discussion, you can visit the Zhihu or Juejin versions of this article for likes and discussion:
About Me && Blog
Below is my personal introduction and related links. I look forward to exchanging ideas with fellow professionals. “When three walk together, one can always be my teacher!”
- Blogger Introduction : Contains personal WeChat and WeChat group links.
- Blog Content Navigation : A navigation guide for my blog content.
- Curated Excellent Blog Articles - Android Performance Optimization Must-Knows : Welcome self-recommendations and recommendations (WeChat private chat is fine).
- Android Performance Optimization Knowledge Planet: The paid community stopped operating in 2026 and no longer accepts new members; this link preserves its history.
One walks faster alone, but a group walks further together.
