Android profiler: application startup time, cold start, hot start, warm start

android official website: application startup time

Users expect apps to be responsive and load quickly. Apps that take too long to launch fail to meet this expectation and may disappoint users. This poor experience can lead to users giving your app a low rating on the Play Store or even abandoning your app entirely.

This document provides information to help you optimize your app's startup time. First, we introduce the internal mechanics of the startup process. Then, we discuss how to analyze startup performance. Finally, some common startup time problems are covered and some tips on how to resolve them are given.

1. Understand different application startup states

Apps have three startup states, each of which affects how long it takes to appear to the user: cold, warm, or warm. In a cold start, the application is started from scratch. In the other two states, the system needs to bring applications running in the background to the foreground. It is recommended that you always optimize on the assumption of a cold start. Doing so can also improve warm start and hot start performance.

To optimize your app for fast launch, it's helpful to understand what's going on at the system and app levels and how they interact in each state.

1. Cold start

Cold start means that the application is started from scratch: the system process only creates the application process after the cold start. A cold start occurs when an app is launched for the first time since the device started or the system terminated the app. This boot poses the greatest challenge in minimizing boot time because the system and applications have more work to do than in the other two boot states.

At the beginning of a cold start, the system has three tasks, namely:

  • Load and start the application.
  • Displays the app's blank launch window immediately after launch.
  • Create application process.

Once the application process is created by the system, the application process is responsible for the following stages:

  • Create application object.
  • Start the main thread.
  • Create the main activity.
  • Expand view.
  • Layout screen.
  • Perform initial drawing.
    Once the application process completes the first drawing, the system process will replace the currently displayed background window and replace it with the main activity. At this point, the user can start using the app.

Insert image description here

Figure 1 shows how work is handed over between system processes and application processes.

Figure 1. Visual representation of important parts of application cold start.
Performance issues may arise during the process of creating applications and creating activities.

Application creation

When the app starts, the blank launch window remains on the screen until the system finishes painting the app for the first time. Once completed, the system process replaces the app's startup window, allowing the user to begin interacting with the app.

If you replace Application.onCreate() in your app, the onCreate() method will be called on the application object. Afterwards, the application generates the main thread (also called the interface thread) and uses it to perform the task of creating the main activity.

From this point on, system-level and application-level processes continue to run based on the application lifecycle stage.

activity creation

After the activity is created in the application process, the activity performs the following operations:

  • ①Initialization value.
  • ②Call the constructor.
  • ③According to the current life cycle state of the activity, call the callback method accordingly, such as Activity.onCreate().
    Typically, the onCreate() method has the greatest impact on load time because it performs the most expensive work: loading and inflating the view, and initializing the objects needed to run the activity.

2. Warm start

A warm start includes some of the operations that occur during a cold start; at the same time, its overhead is higher than a warm start. There are many potential states that can be considered a warm start. For example:

  • The user exits the app and then relaunches the app. The process may continue to run, but the app must re-create the activity from scratch by calling onCreate().

  • The system evicts your app from memory, and the user restarts it. The process and activity need to be restarted, but the saved instance state bundle passed to onCreate() helps with this task.

3. Hot start

Warm startup of an application is much simpler and less expensive than cold startup. In a warm start, all the system's job is to bring your activity to the foreground. As long as all of your app's activities remain in memory, your app doesn't have to repeatedly perform object initialization, layout inflation, and rendering.

However, if some memory is completely cleared in response to a memory trimming event (such as onTrimMemory()), the corresponding objects need to be recreated in response to a warm start event.

A warm boot displays the same on-screen behavior as a cold boot scenario:

The system process will display a blank screen until the app finishes rendering the activity.

Insert image description here

Figure 2. This diagram shows the various startup states and their respective processes, each of which will begin with the first frame drawn.

2. Use indicators to detect and diagnose problems

To properly diagnose startup time performance, you can track some metrics that show how long it takes for your app to start. Android provides several ways to let you know there's a problem with your app and help you diagnose it. Android vitals can alert you that a problem is occurring, and diagnostic tools can help you diagnose it.

Advantages of using startup indicators

Android uses the time to initial display and time to full display metrics to optimize cold and warm app launches. The Android Runtime (ART) uses data from these metrics to efficiently precompile code to optimize future launches.

Faster launches promote continued user interaction with your application, reducing premature exits, instance restarts, or trips to other applications.

Android Vitals

Android Vitals can help improve app performance by alerting you through the Play Console when your app takes too long to launch. Android Vitals considers your app's startup time to be too long when:

  • Cold start took 5 seconds or more.
  • Warm start took 2 seconds or more.
  • Warm start took 1.5 seconds or more.
    Android vitals uses a preliminary display of elapsed time metrics. To learn how Google Play collects Android vitals data, see the Play Console documentation.

Preliminary display of elapsed time

The Time to Initial Display (TTID) metric measures the time it takes for your app to generate the first frame, including process initialization (if a cold start), activity creation (if a cold/warm start), and displaying the first frame.

How to retrieve the TTID

In Android 4.4 (API level 19) and higher, Logcat contains an output line that contains a value named Displayed. This value represents the time it takes from starting the process to completing drawing of the corresponding activity on the screen. Elapsed time consists of the following sequence of events:

  • Start the process.
  • Initialize the object.
  • Create and initialize the activity.
  • Expand layout.
  • Draw the application for the first time.
    The reported log lines resemble the following example:
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms

Finding the elapsed time is simple if you follow the Logcat output from the command line or in the terminal. To find elapsed time in Android Studio, the filter must be disabled in the logcat view. Disabling the filter is necessary because it is the system server that provides this log, not the application itself.

Once you have the correct settings, it's easy to search for the correct terms to see the time. Figure 2 shows a sample Logcat output showing how to deactivate the filter, with the Displayed time shown on the second-to-last line of the output.

Insert image description here

Figure 2. Disable the filter and look for the "Displayed" value in Logcat.
The Displayed metric in Logcat output does not necessarily capture time until all resources are fully loaded and displayed: it omits resources that are not referenced in the layout file or that the app created as part of object initialization. It excludes these resources because loading them is an inline process and does not prevent the initial display of the app.

Occasionally, the Displayed line in the logcat output contains an additional field to display the total time. For example:

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)

In this case, the first time measurement is only for the first activity drawn. The total time measurement is measured from when the app starts, and can include another activity that starts for the first time but doesn't display anything on the screen. The total time measurement is displayed only if there is a difference between the individual activity's time and the total startup time.

You can also run the application using the ADB Shell Activity Manager command to measure the initial display time. Examples are as follows:

adb [-d|-e|-s <serialNumber>] shell am start -S -W
com.example.app/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN</pre>

Displayed metrics appear in logcat output as before. Your terminal window should also display the following:

Starting: Intent
Activity: com.example.app/.MainActivity
ThisTime: 2044
TotalTime: 2044
WaitTime: 2054
Complete

-c and -a are optional parameters that allow you to specify and

Full display of elapsed time

The Time to Full Display (TTFD) metric measures the time it takes for your app to generate the first frame with complete content, including content that is loaded asynchronously after the first frame. Typically, this is the main list content loaded from the network (as reported by the app).

How to retrieve TTFD

You can use the reportFullyDrawn() method to measure the time it takes from app startup to when all resources and view hierarchies are fully displayed. This data can be useful when your app performs lazy loading. In lazy loading, the app does not block the initial drawing of the window, but loads resources asynchronously and updates the view hierarchy.

If the initial display of your app does not include all resources due to lazy loading, you might consider fully loading and displaying all resources and views as a separate metric: for example, your UI might be fully loaded with some text drawn, but not yet Displays images that the application must pull from the web.

To solve this problem, you can manually call reportFullyDrawn() to let the system know that your activity has completed lazy loading. When you use this method, the value displayed by logcat is the time elapsed from the creation of the application object to the time reportFullyDrawn() is called. The following is an example of logcat output:

system_process I/ActivityManager: Fully drawn {
    
    package}/.MainActivity: +1s54ms

The logcat output sometimes includes the total time, as described in Preliminary display of elapsed time.

If you find that your display times are longer than you would like, you can continue to try to identify bottlenecks in the startup process.

Identify bottlenecks

A good way to find bottlenecks is to use the Android Studio CPU Profiler. For related information, see Checking CPU Activity Using the CPU Performance Analyzer.

You can also gain insight into potential bottlenecks through embedded tracing inside the onCreate() methods of your apps and activities. To learn about embedded tracing, see the Trace function documentation and System Tracing Overview.

3. Pay attention to common problems

This section discusses several issues that commonly affect application startup performance. These issues mainly involve initializing application and activity objects, and loading screens.

Intensive application initialization

When your code replaces the Application object and performs intensive work or complex logic during initialization of the object, startup performance may be affected. If your app subclass performs initialization that doesn't yet need to be completed, your app might waste time during startup. Some initialization may be completely unnecessary: ​​for example, initializing the main activity's state information is unnecessary when the application has actually started in response to the intent. With intents, the application uses only a subset of the previously initialized state data.

Other challenges during application initialization include large or numerous garbage collection events, or disk I/O that occurs concurrently with initialization and further blocks the initialization process. Garbage collection is a special consideration for the Dalvik runtime; the Art runtime performs garbage collection simultaneously, minimizing the impact of this operation.

Diagnose the problem

You can use method tracing or inline tracing to try to diagnose the problem.

Method trace record

Running the CPU Profiler shows that the callApplicationOnCreate() method ultimately calls your com.example.customApplication.onCreate method. If the tool shows that these methods are taking a long time to complete execution, you should explore further to see what work is being done.

Embedded trace logging

Use embedded trace logging to investigate possible sources of problems, including:

  • The application's initial onCreate() function.
  • Any global singleton objects that are initialized by the application.
  • Any disk I/O, deserialization, or tight loops that may occur during the bottleneck.

problem solution

Whether the problem is unnecessary initialization or disk I/O, the solution is lazy initialization. In other words, you should only initialize objects that are needed immediately. Adopt the singleton pattern so that your application only initializes an object when it is needed for the first time, rather than creating a global static object.

Additionally, consider using a dependency injection framework such as Hilt, which creates objects and dependencies on first injection.

If your app uses a content provider to initialize app components on startup, consider using the App Startup library instead.

Intensive activity initialization

Creating activities often requires a lot of expensive work. There are often opportunities to optimize this work to achieve performance improvements. Such frequently asked questions include:

  • Expand large or complex layouts.
  • Blocks screen drawing or network I/O on disk.
  • Load and decode bitmaps.
  • Rasterize a VectorDrawable object.
  • Initialize other subsystems of the activity.

Diagnose the problem

It turns out that method tracing and inline tracing are equally useful in this situation.

Method trace record

When using the CPU Profiler, pay attention to the application's Application subclass constructor and com.example.customApplication.onCreate() method.

If the tool shows that these methods are taking a long time to complete execution, you should explore further to see what work is being done.

Embedded trace logging

Use embedded trace logging to investigate possible sources of problems, including:

  • The application's initial onCreate() function.
  • Any global singleton objects that are initialized by the application.
  • Any disk I/O, deserialization, or tight loops that may occur during the bottleneck.

problem solution

There are many potential bottlenecks, but two common problems and remedies are as follows:

  • The larger your view hierarchy is, the longer it will take your app to inflate it. The two steps to resolve this issue are:
    • Flatten your view hierarchy by reducing redundant or nested layouts.
    • Rather than inflating portions of the interface that don't need to be displayed during startup, use ViewStub objects as placeholders for subhierarchies that your app can inflate at more appropriate times.
  • Doing all resource initialization on the main thread also slows down startup. You can resolve this issue as follows:
    • Offload all resource initialization so that the app can defer execution on other threads.
    • Allows the app to load and display your view and later update visual properties that depend on bitmaps and other resources.

Customize splash screen

If you have previously used one of the following methods to implement a custom splash screen in Android 11 (API level 30) or lower, you may experience additional startup time:

  • Use the windowDisablePreview theme property to turn off the initial blank screen that the system draws during startup.

  • Use dedicated activities.
    Starting with Android 12, it is necessary to migrate to the SplashScreen API. This API improves startup time and allows you to adjust the splash screen by:

  • Set a theme to change the look of your splash screen

  • Control how long the splash screen is displayed

  • Determine how much extra time the splash screen animation requires and handle the animation appropriately to close the splash screen.
    Additionally, the compat library backports the SplashScreen API to support backward compatibility and achieve consistent splash screen display across all Android versions.

For more information, see the splash screen migration guide.

Guess you like

Origin blog.csdn.net/sinat_31057219/article/details/132452043