General startup optimization of Android performance optimization

Reprinted from: https://juejin.im/post/5874bff0128fe1006b443fa0

I. Introduction

With the iteration of the project version, the performance problems of the App will gradually be exposed, and good user experience is closely related to performance. Starting from this article, I will start a topic on Android application performance optimization, from theory to practice, from From getting started to digging deeply, and putting performance optimization into the project hands-on, you are welcome to continue to pay attention!

So in the first article, I will start with the startup optimization of the application, and create a lightning-fast App startup speed according to actual cases.

2. Accelerate the start of the first acquaintance

Take a look at Google's official document "Launch-Time Performance" for an overview of application startup optimization;

Application startup is divided into cold startup, warm startup, and warm startup. The slowest startup and the biggest challenge is the cold startup: the system and the app itself have more work to start from scratch! Before an application cold starts, it performs three tasks:

  1. Load the startup App;
  2. A blank Window is displayed immediately after the App starts;
  3. The process of creating an App;

After these three tasks are executed, the following tasks will be executed immediately:

  1. Create an App object;
  2. Start Main Thread;
  3. Create the activated Activity object;
  4. Load View;
  5. layout the screen;
  6. Do the first drawing;

Once the App process completes the first drawing, the system process will replace the displayed Background Window with the Main Activity, and the user can use the App.

Application cold start flow chart As a common application, we cannot actively control the creation of the App process and other links. What can be optimized is the process of Application, Activity creation, and callback .

Similarly, Google also gave directions for starting acceleration :

  1. Use the Window displayed in advance to quickly display an interface and give users a quick feedback experience;
  1. Avoid heavy app initialization at startup;
  2. Positioning problems: Avoid I/O operations, deserialization, network operations, layout nesting, etc.

Remarks: Direction 1 is a temporary solution, but it is only superficially fast; directions 2 and 3 can actually speed up the startup speed. Next, we will actually apply it in the project.

3. The theme switching of startup acceleration

Follow the instructions in the official documentation: Use the Activity's windowBackground theme property to provide a simple drawable for the launched Activity. Layout XML file:

Resource file configuration Manifest file:

Manifest file

in Activity

In this way, when starting, an interface will be displayed first. This interface is the Style set in the Manifest. After the Activity is loaded, the interface of the Activity will be loaded. In the interface of the Activity, we will reset the theme to the normal theme. , resulting in a fast feeling . However, as summarized above, this method does not really accelerate the startup process, but optimizes the display effect through interactive experience. Note: The screenshots are also from the official document "Launch-Time Performance" .

4. Avoid Heavy App Initialization of Startup Acceleration

Through code analysis, we can get the business workflow diagram started by the App:

App cold start business flow chart

In this chapter, we focus on the initialization part: in the Application and the first screen Activity, we mainly do:

  • The initialization of MultiDex and Tinker is executed first; the optimization of MultiDex will not be repeated in this article, please refer to my previous series of articles on Multidex .
  • Application mainly does the initialization of various three-party components;

In the project, all the other three-party components except Tingyun seize the opportunity and initialize in the main thread of Application. **Initialization like this is definitely overkill:

  • Consider initializing three-party components asynchronously without blocking the main thread;
  • Delay the initialization of some third-party components; in fact, we put all the third-party components into asynchronous tasks at a coarse level, and there may be errors that have not been initialized in WorkThread but have been used in MainThread, so it is recommended to delay until before use in this case. to initialize;
  • And how to open WorkThread is also very important. This topic is discussed in detail below.

Project modification:

  1. Put Umeng, Bugly, Tingyun, GrowingIO, BlockCanary and other components in WorkThread to initialize;
  2. Delay the initialization of components such as map positioning, ImageLoader, and its own statistics: the map and its own statistics are delayed for 4 seconds, and the application has been opened at this time; and ImageLoader cannot be asynchronous and delayed too long because of the calling relationship, and the initialization is delayed from Application to SplashActivity; and EventBus Because it is used in Activity, it must be initialized in Application.

Three-party component call optimization sample code

Note: The 2-second pause of the splash screen page can be used to delay the time-consuming operation to this time interval.

5. Diagnosing The Problem of Startup Acceleration

In this section, we actually locate time-consuming operations. In the development phase, we generally use BlockCanary or ANRWatchDog to find time-consuming operations, which are simple and clear, but we cannot get the execution time of each method and more detailed comparison information . We can obtain more comprehensive and detailed information through Method Tracing or DDMS . Start the application, click Start Method Tracing, click again after the application is started, and the .trace file recorded by the operation just now will be automatically opened. It is recommended to use DDMS to view, the function is more convenient and comprehensive.

Application startup trace file analysis diagram before optimization

The left side is the specific thread of the occurrence, the right side is the time axis of the occurrence, and the following is the specific method information of the occurrence. Note two columns: Real Time/Call (actual time of occurrence), Calls+RecurCalls/Total (number of occurrences); we can get the following information in the above figure:

  • It can be seen intuitively that the timeline of MainThread is very long, indicating that most tasks are executed in MainThread;
  • By sorting in descending order of Real Time/Call, you can see that some of the code in the program is really time-consuming;
  • It can be seen on the next page that some third-party SDKs are also time-consuming;

Even if it is a time-consuming operation, as long as it happens correctly in the WorkThread , there is no problem. So we **need to confirm which thread these methods execute and when. If these operations occur in the main thread, it may not constitute the occurrence condition of ANR, but the freeze is inevitable! **Combining with the business operations and analysis diagrams in the App cold start business workflow diagram in the previous chapter, and looking at the code again, we can see that some time-consuming operations such as IO reading do occur in the main thread. In fact, clicking on the name of the execution function in the traceview can not only track the method time-consuming of the parent class and subclass, but also see which thread and the time-consuming interface flashing in the method execution timeline.

It is analyzed that some time-consuming operations occur in the main thread, so if we change all the time-consuming operations to sub-threads, everything will be fine? Not too! !

  • Caton can not be solved by asynchronous. Wrong use of engineering thread not only can not improve the caton, but may aggravate the caton. Whether it is necessary to start a worker thread needs to be analyzed according to the specific performance bottleneck root cause, and the right medicine cannot be generalized;
  • How to start a thread is also learned: Thread, ThreadPoolExecutor, AsyncTask, HandlerThread, IntentService, etc. have their own advantages and disadvantages; for example, ThreadPoolExecutor is usually more efficient and has obvious advantages than Thread, but the performance of Thread at a single point in time in specific scenarios will be better than ThreadPoolExecutor : For the same object creation, the overhead of ThreadPoolExecutor is significantly larger than that of Thread;
  • Correctly starting a thread cannot cure all problems. For example, executing a network request will create a thread pool, and creating a thread pool correctly in an Application will inevitably slow down the startup speed; therefore, delay operations are also essential.

Through the detailed tracing of the traceview and the detailed comparison of the code, I found that the stuck occurs in :

  • Some database and IO operations occur in the main thread of the first screen Activity;
  • A thread pool is created in Application;
  • The first screen Activity network requests are intensive;
  • Worker threads use unset priorities;
  • The information is not cached, and the same information is obtained repeatedly;
  • Process problems: For example, the splash screen image is downloaded every time and used at the same time;

and other details:

  • Execute useless old code;
  • Execute the code used in the development phase;
  • perform repetitive logic;
  • Call the redundant code in the third-party SDK or Demo;

Project modification: 1. The database and IO operations are moved to the worker thread, and the thread priority is set to THREAD_PRIORITY_BACKGROUND, so that the worker thread can get up to 10% of the time slice, and the main thread is given priority to execute.

2. Sort out the process and delay the execution; in fact, this step is the most effective for accelerating the project start-up. Through process sorting, it is found that some process calls are too early or wrong, for example:

  • Updates and other operations do not need to be called before the first screen is displayed, resulting in resource competition;
  • The switch made by IOS to avoid auditing is called, resulting in intensive network requests;
  • Self-statistics create a fixed number of 5 thread pools in the call of the Application, resulting in resource competition. In the last line of the traceview function description in the figure above, you can see that the number 12 is executed 5 times, and the time-consuming ranking is in the forefront; here the thread pool's Creation is necessary but can be deferred.
  • Modify the advertising splash screen logic to take effect next time.

3. Other optimizations;

  • Remove old code that is useless but executed;
  • Remove the code used in the development phase but executed online;
  • Remove repetitive logic execution code;
  • Remove the redundant code that calls the third-party SDK or Demo;
  • Information cache, commonly used information is only obtained for the first time, and then retrieved from the cache;
  • The project is a multi-process architecture, and only executes Application's onCreate() in the main process;

Business code optimization example

Through the above three steps and the optimization of the three-party components: During the callback of Application and Activity on the first screen, the main thread does not take time and compete for resources. In addition, some technologies such as layout optimization and memory optimization are also involved . Because cold start of applications is generally not a bottleneck, it will not be discussed in detail here, and it can be handled according to actual projects.

6. Contrast effect:

Use the ADB command to count the startup time of the application: adb shell am start -W Activity above the fold. Use MX3 and Nexus6P under the same conditions, start 5 times, and compare the startup time before and after optimization;

Before optimization: MX3

ThisTime TotalTime WaitTime
1237 2205 2214
1280 2181 2189
1622 2508 2513
1485 2434 2443
1442 2418 2429

Nexus6P

ThisTime TotalTime WaitTime
1229 1832 1868
1268 1849 1880
1184 1780 1812
1262 1845 1876
1164 1766 1807


After optimization: MX3

ThisTime TotalTime WaitTime
865 1516 1523
911 1565 1573
812 1406 1418
962 1564 1574
925 1566 1577

Nexus6P

ThisTime TotalTime WaitTime
603 1192 1243
614 1076 1115
650 1120 1163
642 1107 1139
624 1084 1124


Comparison: MX3 improved by 35%

ThisTime Average Average TotalTime Average WaitTime
Before optimization 1413 2349
Optimized 895 1523


Nexus6P increased by 39%

ThisTime Average Average TotalTime Average WaitTime
Before optimization 1221 1814
Optimized 626 1115
  • 命令含义:ThisTime:最后一个启动的Activity的启动耗时;TotalTime:自己的所有Activity的启动耗时;
    WaitTime: ActivityManagerService启动App的Activity时的总时间(包括当前Activity的onPause()和自己Activity的启动)。

七、问题:

1、还可以继续优化的方向?

  • 项目里使用Retrofit网络请求库,FastConverterFactory做Json解析器,TraceView中看到FastConverterFactory在创建过程中也比较耗时,考虑将其换为GsonConverterFactory。但是因为类的继承关系短时间内无法直接替换,作为优化点暂时遗留;
  • 可以考虑根据实际情况将启动时部分接口合并为一,减少网络请求次数,降低频率;
  • 相同功能的组件只保留一个,例如:友盟、GrowingIO、自有统计等功能重复;
  • 使用ReDex进行优化;实验Redex发现Apk体积确实是小了一点,但是启动速度没有变化,或许需要继续研究。

2、异步、延迟初始化及操作的依据?注意一点:并不是每一个组件的初始化以及操作都可以异步或延迟;是否可以取决组件的调用关系以及自己项目具体业务的需要。保证一个准则:可以异步的都异步,不可以异步的尽量延迟。让应用先启动,再操作。

3、通用应用启动加速套路?

  • 利用主题快速显示界面;
  • ** 异步初始化组件;**
  • ** 梳理业务逻辑,延迟初始化组件、操作;**
  • ** 正确使用线程;**
  • ** 去掉无用代码、重复逻辑等。**

4、其它

  • Speeding up the startup speed by 35% does not mean that the previous code is a problem. From a business point of view, the code is not wrong, and the business requirements are achieved. But in the speed-focused phase of startup, neglected details can lead to performance bottlenecks.
  • During the development process, use TraceView to analyze the core modules and application phases such as startup to find bottlenecks as soon as possible.

Reference article: "Official Documentation - Launch-Time Performance"


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324699846&siteId=291194637