What is a second

The Android App starts in seconds. In a narrow sense, it means that your App’s Activity takes less than one second from launch to display. In a broad sense, it means that the process takes as little time as possible. The shorter this time is, the more responsive your App will appear to the user, the smoother it will be, and the better the user experience. Seconds is an important performance indicator for Android apps. Need our continuous attention and optimization.

How to optimize second opening

Google provides a lot of performance optimization advice and official tools, and there are a lot of articles and tools on the Web about Android App performance optimization, which can help you solve most of the problems. But the reality may be that your App takes too long to start a new Activity, even if you put a lot of effort into optimizing it. Especially as the demand grows, your App becomes complex and large, and the first step to optimizing is to locate the points that need to be optimized, which becomes more and more difficult. At the same time, large apps are increasingly likely to take too much time to start new activities.

Among the tuning recommendations, a basic rule is to avoid time-consuming operations in the main thread (or UI thread) as much as possible. Examples include file read and write operations, network requests, heavy computations, loops, and so on. This is intuitive because starting a new Activity requires a lot of code to be executed on the main thread, such as lifecycle callbacks such as onCreate(). If there are time-consuming operations being executed on the main thread, the time it takes to display the new Activity is increased. To optimize seconds, you first need to be able to monitor the running state of the main thread, so the question is, how is the main thread running? When and how does your code get executed on the main thread?

Go deep into the main thread

To understand how the main thread works, you need to understand Android’s messaging mechanism.

Message mechanism

Let’s take a look at an example in real life. Although mobile payment is now available, I believe everyone has gone to the bank to withdraw money. When you arrive at the bank, if you are the first one, congratulations, you can go to the teller immediately to do your business. If there are other people in front of you, it’s worse. You have to wait in line until everyone in front of you finishes their business. What’s more terrible is that if you have several people in front of you who need to deal with the business takes a long time, then you need to wait longer; Others will line up behind you, just as impatiently wondering when their turn will come.

In abstraction, the messaging mechanism is very similar to this example. Everyone sees it as a message, and when the bank arrives is uncertain. A teller can be seen as a message processor. He handles your business as if he were processing your messages. A queue in which people line up in order can be thought of as a message queue. So this process can be abstracted as if you have a message handler, and it has a message queue, and the messages that come in at random are in that queue in a certain order, and the message handler keeps getting messages from the head of the queue and processing them, and doing this over and over again. As shown below:




Message mechanism


So how does Android implement this messaging mechanism?

Android messaging mechanism

The messaging mechanism has to have a Message in the first place, which in Android is Message. How do you determine a message? The message must have a source or target, i.e. Target; The message indicates what it wants to do, that is, what or callback; The message indicates when it wants to be executed, that is, when. With these elements, the message is basically a complete message that can be added to the message queue. The MessageQueue in Android is MessageQueue. The message processing loop is a Looper. Looper is an endless loop that retrieves messages from MessageQueue and processes them. This is done in a Handler. Adding a message to a message queue also requires a Handler. Message, MessageQueue, Looper, and Handler together make up the entire Android messaging mechanism.

Android’s main thread runs such a messaging mechanism.

The main thread of Android

The main thread is created in ActivityThread, as you can see in the main function

public static void main(String[] args) { ... Looper.prepareMainLooper(); . Looper.loop(); }Copy the code

The main thread implements a messaging mechanism. So Android’s main thread is a message processing loop. All it does is get messages from the message queue, process them, and so on and so forth. All of your App’s UI actions, such as click event handling, page animation, displaying updated pages, View drawing, and launching new activities, send messages to the main thread, which then processes these messages one by one.

How does the main thread affect seconds

Once we understand how the main thread works, we need to look at how message processing in the main thread affects the Activity’s second start. When we start a new Activity, the Android system sends a series of messages to the main thread from the time we call startActivity until the new Activity is displayed. The total time it takes to process this series of messages affects the page’s opening seconds, and if it takes too long, the user will feel very slow to respond. In addition, the Android system will send messages to the main thread, and the App itself will also send messages to the main thread. If the App’s own messages are inserted into the series of Android system messages during the process of starting a new Activity, the total processing time will be prolonged, resulting in a delay in starting the Activity.




Second drive signal


The figure above shows three cases of the main thread starting a new Activity. Each rectangle represents how long it takes the main thread to process a message, with wider rectangles representing longer processing times. The green stuff means this is a message from the Android system; The blue padding means that this is a message sent by the App itself. The bottom right arrow represents the time, starting at the time startActivity was called.

  • The first represents the normal situation in which only startActivity-related system messages are processed in the main thread, and each message takes a reasonable amount of time to process. So this page can satisfy the second open.
  • The second case represents an exception where the main thread processes all system messages, but the processing time of one or more messages exceeds a reasonable value, causing the page to not open in seconds.
  • The third case represents another abnormal situation, in which the App’s own messages are mixed into the system messages. The main thread not only needs to process the system messages, but also the App’s own messages. As a result, the total startup time is added to the App message processing time, resulting in the page cannot open in seconds. In practice, it may take too long for both system messages to be processed and the App’s own messages to be mixed in.

Second drive optimization

Once we understand the factors that affect seconds, as long as we have a way to monitor the processing time of each message in the main thread, we can locate the cause of the slow page and then optimize it. Fortunately, the Android engineers have reserved a log slot for us in Looper.

public static void loop() { final Looper me = myLooper(); . final MessageQueue queue = me.mQueue; . for (;;) { Message msg = queue.next(); // might block ... Printer logging = me.mLogging; if (logging ! = null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging ! = null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); }... msg.recycleUnchecked(); } } public void setMessageLogging(@Nullable Printer printer) { mLogging = printer; }Copy the code

You can see that the log is printed at the beginning of the message processing and at the end of the message processing. All you need to do is call Looper.setMessagelogging () in your code to set it up.

Looper.getMainLooper().setMessageLogging(new Printer() { @Override public void println(String s) { Log.v("debug", s); }});Copy the code

Compile and run your program, and you’ll see something like this in logcat output:




Message logging


The log at the beginning of “>>>>> Dispatching to” in each row indicates that a message is about to be processed. The log at the beginning of the next line <<<<< Finished to indicates that the message has been processed. From these logs you can know all the messages that were processed by the main thread and how long each message took based on the time difference between the start and end. With this information you can find messages that are causing your app to slow down and further debug the problem.

You can look at the log output when you start a new Activity and see if there are any messages that take a long time to process, or if there are any messages that are processed by the App itself. If so, these are all points that need to be optimized.

However, the disadvantages of looking at the log directly are that there are many logs, and it is not easy to locate the start and end time of the Activity. In addition, it is not very intuitive to calculate the time of each message processing.

StallBuster

In order to facilitate the second start optimization, I made a tool called StallBuster to help locate the cause of the failure of the Activity to start in seconds.

Integrating StallBuster is very simple and only takes two steps

  1. Add a dependency on StallBuster
Dependencies {the compile 'com. Making. Zhangjianli: stallbuster: 1.1'}Copy the code
  1. Add the following code to your App’s Application
public class YourApplication extends Application { @Override public void onCreate() { StallBuster.getInstance().init(this); super.onCreate(); }}Copy the code

And there you go, compile and run your App. When you open a new Activity in your App, StallBuster will send a Notification. Tells you how many milliseconds it took to start the Activity




notification


Clicking on this Notification brings up the StallBuster history page.




records




Details page


In the details page you can see the messages that were processed by the main thread when the Activity was started. The check box in the upper part can filter messages with short execution time, facilitating fault locating.

For each record, the timestamp is first displayed when the message was first processed. Then there is the Cost field, which indicates how long it took to process the message. Normally the font is black; If the processing time is too long, the color is red. Suggesting that this might be something we need to optimize.

Next comes the Target field, which corresponds to which Handler the message was processed by. Android handlers are displayed in black. The App’s own Handler is shown in red, indicating that this message should not appear when the Activity is started, which may also be an area that needs to be optimized.

For example, the first record in the figure above,.mainActivity $StallHandler took 142ms to process the message. This increases the time it takes to start SubActivity by at least 142ms. And this Handler is going to be your App’s own Handler. We need to debug the code so that when we start the Activity there are no messages from this Handler, and 142ms of time will be saved.

The last field is message or callback. This corresponds to what or callback in Message. With this information, we can easily locate the messages that affect seconds in the main thread and optimize our App.

StallBuster is here to introduce you, I hope StallBuster can help you. Please leave a comment if you have any suggestions or questions.

conclusion

App seconds is a very important performance indicator. Second on optimization is a complex work, there are many factors will affect the second on App. One of the more important factors is how the main thread handles messages when the Activity is started. You need to avoid message processing that takes too long during the Activity startup process, and you need to avoid having your App’s own messages processed during this time. The key point of optimization is to locate time-consuming operations in the main thread. We can do this by printing the message processing log that analyzes the main thread, but this method is not very intuitive and convenient. At this time, you can use StallBuster to help you quickly locate the problem point of second opening, so that second opening optimization becomes more simple.