1. Enable optimization classification

Startup optimizations fall into three categories:

  1. Cold start
  2. Wen started
  3. Warm start

1.1 cold start

Start the App for the first time or after the system kills the App process.

At this point, the following process goes through:

  1. Load and start App;
  2. Display a blank Window;
  3. Creating an App process
    • Create the Application object;
    • Create the main Activity;
    • Inflating the View.
    • View Measure, Layout, Draw;

Among the above steps, the developer can interfere with:

  • Displays a blank window;
  • Create the Application object;
  • Create the main Activity;
  • Inflating the View.
  • View Measure, Layout, Draw;

In addition, the cold start process is more complex and goes through complete steps than warm start and hot start, so once you handle the cold start, warm start and hot start will naturally become better.

1.2 temperature start

After the App starts, the user cuts the App to the background. After a while, the system kills the Activity being displayed in the App, but the process in which the App is running still exists.

At this point, the main Activity lifecycle function calls are made:

  1. onCreate();
  2. onStart();
  3. onResume();

1.3 hot start

After the App is started, the user cuts the App to the background, and after a while, cuts back, the process in which the App is running still exists, and the Activity that the App is displaying is not killed.

At this point, the main Activity lifecycle function calls are made:

  • onResume();

2. Start optimization

As can be seen from the previous analysis, the steps that developers can intervene in during App startup are as follows:

  • Displays a blank window;
  • Create the Application object;
  • Create the main Activity;
  • Inflating the View.
  • View Measure, Layout, Draw;

Now, let’s talk about how to do startup optimization from these aspects.

2.1 Measurement of startup time

There are three methods for detecting startup time:

  1. Android Vitals (Google Play Console);
  2. Logcat
    • By Google (provided By Android Studio By default);
    • Custom (developer manual burial point);
  3. ADB

2.1.1 Android Vitals

The Google Play Console will provide App launch time, but this feature is not really meaningful to the vast majority of Chinese developers, so I won’t go into details.

2.1.2 Logcat

2.1.2.1 By Google (Provided By Android Studio By default)

By default, after an App is launched, Android Studio displays how long the App’s current screen has been started and the total time it has been started. The main reason for the two startup duration here is that: Sometimes, the current interface is not the interface corresponding to the main Activiy, but the interface after the jump. Therefore, Android Studio will display the current screen startup time and total startup time. If the current screen is the screen corresponding to the main Activity, Android Studio will only display one duration, since the current screen startup duration is the same as the total startup duration.

//1. The current interface is the corresponding interface of the main Activity. I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.MainActivity: +1s121ms //2. The current interface is not the interface corresponding to the main Activiy, but the interface after the jump. I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.second.SecondActivity: +296ms (total +2s224ms)Copy the code

Android Studio’s default display of the current App startup duration works for all apps on the current device, so developers who want to compare the startup duration of their own apps can use this method.

Note that the default startup duration display logs provided by Android Studio correspond to Verbose and Displayed tags.

2.1.2.2 Custom (Developer manual burying point)

Customize the Application, override the attachBaseContext() method in the custom Application and record the start time, override the onWindowFocusChanged() method in the MainActivity, And record the end time in it.

Public class MyApplication extends Application {@override protected void attachBaseContext(Context)  base) { super.attachBaseContext(base); LogUtils.recordStartTime(); } } //2. MainActivity public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); LogUtils.recordEndTime(); // He/he/he/he/he/he/he/he/he/he/he/he/he/he/he/he/he/he/he/he/he/he/he/he 486 //4. By default, Android Studio provides // The system computing start time is longer than the developer manual buried point calculation mainly because the system computing start time includes: //- start App //- display blank Window //- create App process // I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.MainActivity: +885msCopy the code

2.1.3 the ADB

In addition to the previous two methods, we can also use ADB to get the start time of the specified Activity.

Adb shell am start -s -w package/activity name //2. Sample the adb shell am start - S - W com. Smart. A15_start_up_optimization/com. Smart. A15_start_up_optimization. MainActivity Stopping: com.smart.a15_start_up_optimization Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.smart.a15_start_up_optimization/.MainActivity } Status: ok Activity: com.smart.a15_start_up_optimization/.MainActivity ThisTime: 479 TotalTime: 479 WaitTime: //3. Log //3. 创 办 办 户 包 含 提 升 2019-09-12 16:01:46.069 5315-5315/com.smart. I/ActivityManager: I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.MainActivity: +479msCopy the code

ThisTime: Duration for starting the current Activity. When the App is started, when the interface displayed in the App is not the interface corresponding to the main Activiy but the interface after the jump, ThisTime is the startup time of the Activity corresponding to the current interface, which is different from TotalTime. When the App starts, when the interface displayed in the App is the interface corresponding to the main Activiy, this time is the time used to start the main Activity, and it will be the same as TotalTime.

ADB ADB shell am start -S -W com.smart.a15_start_up_optimization/com.smart.a15_start_up_optimization.MainActivity Stopping: com.smart.a15_start_up_optimization Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.smart.a15_start_up_optimization/.MainActivity } Status: ok Activity: Com. Smart. A15_start_up_optimization /. Second. SecondActivity ThisTime: 170 / / TotalTime SecondActivity start time: 818 // How long does it take to load and start the App until SecondActivity starts WaitTime: 548 / / start time used to main Activity Complete zhangjihuidembp: MyApplication2019CustomView zhangjianhui $1.2 / / Log Android Studio provides by default The 2019-09-12 16:18:36. 757, 1510-1536 /? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.second.SecondActivity: +170ms (total +818ms)Copy the code

TotalTime: How long it takes to start the first Activity in the App that can interact with the user. When the App starts, when the interface displayed in the App is not the interface corresponding to the main Activiy but the interface after the jump, this TotalTime will be different from ThisTime. When the App starts, when the interface displayed in the App is the interface corresponding to the main Activiy, ThisTime is the time used to start the main Activity, and it will be the same as ThisTime.

WaitTime: The time it takes to start the first Activity, from the time the App is loaded, to the time it finally displays the main Activity. Therefore, when the App starts, when the interface displayed in the App is not the interface corresponding to the main Activiy but the interface after the jump, the relationship between the three duration is:

TotalTime > WaitTime > ThisTime

When the App starts, when the interface displayed in the App is the interface corresponding to the main Activiy, ThisTime is the time used to start the main Activity, and it will be the same as ThisTime.

WaitTime > Total = ThisTime

2.2 Start time analysis tool

Android Studio provides two analytics tools for analyzing App startup times:

  1. Traceview.
  2. Systrace;

2.2.1 traceview

2.2.1.1 characteristics
  1. Display execution time, call stack and other information in the form of graphics;
  2. Comprehensive information, including all threads;
2.2.1.2 shortcomings
  1. The runtime overhead is heavy and the whole thing slows down (show all relevant thread information);
  2. The direction of optimization may be biased;
2.2.1.3 Method of use
  1. Debug.startmethodtracing (Constants.TRACE) (Start tracing);
  2. Debug.stopmethodtracing () (stop tracing);
  3. The resulting file in sdcard/Android/data/package name/files/XXX. Trace “;
  4. Open the generated “xxx.trace” file directly in Android Studio to check method calls.
  5. In the “TopDown” column, directly check the call duration of each method, and then find the method that takes the most time, and optimize it accordingly;

For example, if you now want to see how the onCreate() method on SecondActivity executes:

//1. SecondActivity public class SecondActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { //1. Start tracing debug.startMethodTracing (Constants.TRACE); super.onCreate(savedInstanceState);setContentView(R.layout.activity_second); showDeviceMemory(); //2. End trace debug.stopMethodtracing (); } private voidshowDeviceMemory(){
        ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
        int memory = activityManager.getMemoryClass();
        int largeMemory = activityManager.getLargeMemoryClass();
        Log.e(Constants.TAG, "Memory: " + memory + " LargeMemory: " + largeMemory);
        int runtimeTotalMemory = (int)(Runtime.getRuntime().totalMemory() / (1024 * 1024));
        int runtimeFreeMemory = (int)(Runtime.getRuntime().freeMemory() / (1024 * 1024));
        int runtimeMaxMemory = (int)(Runtime.getRuntime().maxMemory() / (1024 * 1024));
        Log.e(Constants.TAG, "Runtime TotalMemory: " + runtimeTotalMemory +
                " Runtime FreeMemory: " + runtimeFreeMemory +
                " Runtime MaxMemory: "+ runtimeMaxMemory); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }}}Copy the code

Trace the path:

Trace analysis:

2.2.2 systrace

2.2.2.1 characteristics
  1. Lightweight, low overhead (only shows buried threads);
  2. Intuitive reflection of CPU utilization;
2.2.2.2 shortcomings
  1. Lightweight, low overhead;
  2. Intuitive reflection of CPU utilization;
2.2.2.3 Method of use
  1. TraceCompat.beginSection(Constants.TRACE); (start tracking);
  2. TraceCompat.endSection(); (stop tracking)
  3. Start the App;
  4. Python systrace.py –time=10 -o mynewtrace.html;
  5. Operate the interface you want to detect in the App;
  6. The resulting file in “file:///Users/xxx/Library/Android/sdk/platform-tools/systrace/mynewtrace.html”;
  7. Open the mynewtrace. HTML file in Chrome to check CPU usage and main thread usage.
  8. In the “Alerts” column, directly locate the possible causes of the deadlock and optimize accordingly;

For example, if you now want to see how the setContentView() method of SixActivity executes:

//1. SixActivity public class SixActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //1. Start tracing tracecompat.beginSection ("SixActivity");
        setContentView(R.layout.activity_six); //2. End tracecompat.endSection (); }}Copy the code

Systrace resolution:

2.3 Optimization of startup time

2.3.1 Display a custom window

  1. Define WindowBackground;
  2. Define the Theme;
  3. Apply the Theme in the AndroidManifest file.
  4. Restore the actual Theme in the Activity;
//1. window_background <? xml version="1.0" encoding="utf-8"? > <layer-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:opacity="opaque"> <! -- The background color, preferably the same as your normal theme --> <item android:drawable="@android:color/white"/ > <! -- Your product logo - 144dp color version of your app icon --> <item> <bitmap android:gravity="center"
            android:src="@drawable/bird_woodpecker" />
    </item>
</layer-list>

//2. WindowBackgroundTheme
<style name="WindowBackgroundTheme" parent="@android:style/Theme.NoTitleBar.Fullscreen">
    <item name="android:windowIsTranslucent">false</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowBackground">@drawable/window_background</item> </style> //3. Apply Theme <? To the AndroidManifest file. xml version="1.0" encoding="utf-8"? > <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.smart.a15_start_up_optimization">

    <application
        android:name=".framework.MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".third.ThirdActivity"
            android:label="@string/third"
            android:theme="@style/WindowBackgroundTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest> //4. Restores the actual Theme public class ThirdActivity extends AppCompatActivity {@override protected void onCreate(Bundle) in the Activity savedInstanceState) { super.onCreate(savedInstanceState);setTheme(R.style.AppTheme);
        setContentView(R.layout.activity_third); }}Copy the code

2.3.2 Application related

Possible problems with custom Application startup:

  1. Heavy initialization work;
  2. Initialize non-required resources;
  3. The I/O;
  4. Frequently initialize the same resource;

Treatment methods:

  1. Asynchronous loading (threads can be created through a thread pool for reuse, and the number of threads created dynamically changes based on the number of CPU cores on the device);
  2. Lazy loading;
  3. Singleton;

Here’s an example:

Public class MyApplication extends Application {@override public voidonCreate() {
        super.onCreate();
        Debug.startMethodTracing(Constants.APPLICATION_TRACE);
        initData();
        Debug.stopMethodTracing();
    }

    private void initData(){//1. Run showDeviceMemory(); //2. Execute the time-consuming operation on the self-thread // newThread(){
//            @Override
//            public void run() {
//                showDeviceMemory();
//            }
//        }.start();
    }

    private void showDeviceMemory(){
        ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
        int memory = activityManager.getMemoryClass();
        int largeMemory = activityManager.getLargeMemoryClass();
        Log.e(Constants.TAG, "Memory: " + memory + " LargeMemory: " + largeMemory);
        int runtimeTotalMemory = (int)(Runtime.getRuntime().totalMemory() / (1024 * 1024));
        int runtimeFreeMemory = (int)(Runtime.getRuntime().freeMemory() / (1024 * 1024));
        int runtimeMaxMemory = (int)(Runtime.getRuntime().maxMemory() / (1024 * 1024));
        Log.e(Constants.TAG, "Runtime TotalMemory: " + runtimeTotalMemory +
                " Runtime FreeMemory: " + runtimeFreeMemory +
                " Runtime MaxMemory: "+ runtimeMaxMemory); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }} // Execute result (execute time operation on main thread) : 2019-09-13 13:51:10.576 1855-1887 /? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: 2019-09-13 + 3 s264ms 13:51:18. 192, 1865-1887 /? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: 2019-09-13 + 3 s333ms 13:51:24. 713, 1865-1887 /? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: +3s319msCopy the code
Public class MyApplication extends Application {@override public voidonCreate() {
        super.onCreate();
        Debug.startMethodTracing(Constants.APPLICATION_TRACE);
        initData();
        Debug.stopMethodTracing();
    }

    private void initData(){showDeviceMemory(); showDeviceMemory(); //2. Execute the time-consuming operation new on the child threadThread(){
            @Override
            public void run() {
                showDeviceMemory();
            }
        }.start();
    }

    private void showDeviceMemory(){
        ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
        int memory = activityManager.getMemoryClass();
        int largeMemory = activityManager.getLargeMemoryClass();
        Log.e(Constants.TAG, "Memory: " + memory + " LargeMemory: " + largeMemory);
        int runtimeTotalMemory = (int)(Runtime.getRuntime().totalMemory() / (1024 * 1024));
        int runtimeFreeMemory = (int)(Runtime.getRuntime().freeMemory() / (1024 * 1024));
        int runtimeMaxMemory = (int)(Runtime.getRuntime().maxMemory() / (1024 * 1024));
        Log.e(Constants.TAG, "Runtime TotalMemory: " + runtimeTotalMemory +
                " Runtime FreeMemory: " + runtimeFreeMemory +
                " Runtime MaxMemory: "+ runtimeMaxMemory); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }} // Execute result (execute time operation on child thread) : 2019-09-13 13:53:41.625 1864-1887 /? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: + 1 s390ms 13:53:44 2019-09-13. 739, 1865-1887 /? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: + 1 s228ms 13:53:47 2019-09-13. 906, 1865-1887 /? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: +1s237msCopy the code
//3. Public Class MyApplication extends Application {// Obtains the number of cpus available on the current device. Private Static final Int CPU_COUNT = Runtime.getRuntime().availableProcessors(); // Ensure that at most four threads and at least two threads are running in the background. Private static final int CORE_POOL_SIZE = math.max (2, math.min (cpu_count-1, 4)); private static ExecutorService executorService; @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); RecordStartTime (); MainActivity logutils.recordStartTime (); } @Override public voidonCreate() {
        super.onCreate();
        Debug.startMethodTracing(Constants.APPLICATION_TRACE);
        initData();
        Debug.stopMethodTracing();
    }

    private void initData(){showDeviceMemory(); showDeviceMemory(); //2. Execute the time-consuming operation on the self-thread // newThread(){
//            @Override
//            public void run() { // showDeviceMemory(); // } // }.start(); / / 3. Custom thread pool, so that the thread resources can be reused executorService = Executors. NewFixedThreadPool (CORE_POOL_SIZE); executorService.submit(newRunnable() {
            @Override
            public void run() { showDeviceMemory(); }}); } private voidshowDeviceMemory(){
        ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
        int memory = activityManager.getMemoryClass();
        int largeMemory = activityManager.getLargeMemoryClass();
        Log.e(Constants.TAG, "Memory: " + memory + " LargeMemory: " + largeMemory);
        int runtimeTotalMemory = (int)(Runtime.getRuntime().totalMemory() / (1024 * 1024));
        int runtimeFreeMemory = (int)(Runtime.getRuntime().freeMemory() / (1024 * 1024));
        int runtimeMaxMemory = (int)(Runtime.getRuntime().maxMemory() / (1024 * 1024));
        Log.e(Constants.TAG, "Runtime TotalMemory: " + runtimeTotalMemory +
                " Runtime FreeMemory: " + runtimeFreeMemory +
                " Runtime MaxMemory: "+ runtimeMaxMemory); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }} // Execute result (execute time-consuming operation in thread pool) : 2019-09-16 10:40:39.050 185-1887 /? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: + 1 s487ms 10:40:43 2019-09-16. 604, 1865-1887 /? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: + 1 s246ms 10:40:48 2019-09-16. 099, 1865-1887 /? I/ActivityManager: Displayed com.smart.a15_start_up_optimization/.forth.ForthActivity: +1s377msCopy the code

2.3.3 Main Activity related

Possible problems when the main Activity starts:

  1. Load complex layout;
  2. Load Bitmap directly;
  3. Load resources in the main thread;
  4. The I/O;
  5. Initialize the Activity subsystem;

Treatment methods:

  1. Reduce View nesting levels (Inflating View, View Measure, Layout, Draw);
  2. Do not load content that is not displayed;
  3. Lazy loading of Bitmap;
  4. Asynchronous loading;

Here’s an example:

//1. Nested layout implementation <? xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingLeft="@dimen/item_height"
    android:paddingTop="@dimen/padding_medium"
    android:paddingRight="@dimen/item_height"
    android:paddingBottom="@dimen/padding_medium">


    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/avatar"
        android:layout_width="@dimen/padding_ninety_six"
        android:layout_height="@dimen/padding_ninety_six"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="@dimen/padding_ninety_six"
        android:scaleType="centerCrop"
        android:src="@drawable/bird_woodpecker"
        app:civ_border_color="@color/grey_800"
        app:civ_border_width="@dimen/padding_micro_x" />

    <LinearLayout
        android:id="@+id/login_username_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/padding_large"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/login_username" />

        <EditText
            android:id="@+id/login_username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/padding_medium"
            android:background="@null"
            android:hint="@string/login_username_hint"
            android:inputType="text"
            android:maxLines="1"
            android:singleLine="true"
            android:textColor="@color/grey_700"
            android:textCursorDrawable="@drawable/common_edit_text_cursor"
            android:textSize="@dimen/font_micro" />
    </LinearLayout>


    <View
        android:layout_width="match_parent"
        android:layout_height="@dimen/padding_micro_xx"
        android:layout_marginTop="@dimen/padding_small"
        android:layout_marginBottom="@dimen/padding_small"
        android:background="@color/grey_300" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/padding_medium"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/login_password" />

        <EditText
            android:id="@+id/login_password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/padding_medium"
            android:background="@null"
            android:hint="@string/login_password_hint"
            android:inputType="textPassword"
            android:maxLines="1"
            android:singleLine="true"
            android:textColor="@color/grey_700"
            android:textCursorDrawable="@drawable/common_edit_text_cursor"
            android:textSize="@dimen/font_micro" />
    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="@dimen/padding_micro_xx"
        android:layout_marginTop="@dimen/padding_small"
        android:layout_marginBottom="@dimen/padding_small"
        android:background="@color/grey_300" />

    <TextView
        android:id="@+id/login"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/padding_medium"
        android:layout_marginTop="@dimen/padding_large"
        android:layout_marginRight="@dimen/padding_medium"
        android:background="@drawable/ripple_login"
        android:clickable="true"
        android:elevation="@dimen/divider_height"
        android:gravity="center"
        android:paddingTop="@dimen/padding_medium"
        android:paddingBottom="@dimen/padding_medium"
        android:text="@string/login"
        android:textColor="@color/grey_700"
        android:textSize="@dimen/font_small"
        android:textStyle="bold" />

</LinearLayout>
Copy the code
//2. Unnested <? xml version="1.0" encoding="utf-8"? > <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingLeft="@dimen/item_height"
    android:paddingTop="@dimen/padding_medium"
    android:paddingRight="@dimen/item_height"
    android:paddingBottom="@dimen/padding_medium">


    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/avatar"
        android:layout_width="@dimen/padding_ninety_six"
        android:layout_height="@dimen/padding_ninety_six"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="@dimen/padding_ninety_six"
        android:scaleType="centerCrop"
        android:src="@drawable/bird_woodpecker"
        app:civ_border_color="@color/grey_800"
        app:civ_border_width="@dimen/padding_micro_x" />

    <TextView
        android:id="@+id/login_username_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/avatar"
        android:layout_marginTop="@dimen/padding_large"
        android:text="@string/login_username" />

    <EditText
        android:id="@+id/login_username"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/avatar"
        android:layout_marginLeft="@dimen/padding_medium"
        android:layout_marginTop="@dimen/padding_large"
        android:layout_toRightOf="@id/login_username_label"
        android:background="@null"
        android:hint="@string/login_username_hint"
        android:inputType="text"
        android:maxLines="1"
        android:singleLine="true"
        android:textColor="@color/grey_700"
        android:textCursorDrawable="@drawable/common_edit_text_cursor"
        android:textSize="@dimen/font_micro" />


    <View
        android:id="@+id/login_username_divider"
        android:layout_width="match_parent"
        android:layout_height="@dimen/padding_micro_xx"
        android:layout_below="@id/login_username_label"
        android:layout_marginTop="@dimen/padding_small"
        android:layout_marginBottom="@dimen/padding_small"
        android:background="@color/grey_300" />

    <TextView
        android:id="@+id/login_password_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/login_username_divider"
        android:layout_marginTop="@dimen/padding_medium"
        android:text="@string/login_password" />

    <EditText
        android:id="@+id/login_password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/login_username_divider"
        android:layout_marginLeft="@dimen/padding_medium"
        android:layout_marginTop="@dimen/padding_medium"
        android:layout_toRightOf="@id/login_password_label"
        android:background="@null"
        android:hint="@string/login_password_hint"
        android:inputType="textPassword"
        android:maxLines="1"
        android:singleLine="true"
        android:textColor="@color/grey_700"
        android:textCursorDrawable="@drawable/common_edit_text_cursor"
        android:textSize="@dimen/font_micro" />

    <View
        android:id="@+id/login_password_divider"
        android:layout_width="match_parent"
        android:layout_height="@dimen/padding_micro_xx"
        android:layout_below="@id/login_password_label"
        android:layout_marginTop="@dimen/padding_small"
        android:layout_marginBottom="@dimen/padding_small"
        android:background="@color/grey_300" />

    <TextView
        android:id="@+id/login"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/login_password_divider"
        android:layout_marginLeft="@dimen/padding_medium"
        android:layout_marginTop="@dimen/padding_large"
        android:layout_marginRight="@dimen/padding_medium"
        android:background="@drawable/ripple_login"
        android:clickable="true"
        android:elevation="@dimen/divider_height"
        android:gravity="center"
        android:paddingTop="@dimen/padding_medium"
        android:paddingBottom="@dimen/padding_medium"
        android:text="@string/login"
        android:textColor="@color/grey_700"
        android:textSize="@dimen/font_small"
        android:textStyle="bold" />

</RelativeLayout>
Copy the code

The two XML layout files above end up achieving the same effect, with the only difference being that the former has more nested levels and the latter has none. Of course, this is only in a simple layout file, but if it is in a complex layout file, the effect of this optimization is obvious. Therefore, when the layout of the main Activity has too many nesting levels, the startup time of the App will be affected. Therefore, it is imperative to reduce the nesting level of the layout file.

3. Summary

App startup speed is the user’s first experience of the App, so startup is very important.

There are three areas that developers can optimize during App startup:

  1. Create a blank window;
  2. Create the Application;
  3. Create the Activity;

Therefore, the general direction of startup optimization for developers is determined, so when detecting slow App startup, just from these three aspects is enough!


Reference documentation

  1. Launch Time
  2. Iqiyi Android client startup optimization and analysis
  3. Alipay client architecture analysis: Android client startup speed optimization “garbage Collection”