Android teaches you how to find app Caton

Recently, the Department plans to optimize the blocking situation of APP on low-end machines. Since you want to optimize, you must obtain the blocking situation, so how to obtain the blocking situation is the purpose of this paper.

Generally, too many UI drawing, a large number of IO operations or a large number of computing operations of the main thread occupy the CPU, resulting in the app interface jamming. As long as we can capture the stack information of the main thread and the resource usage information of the system when the jam occurs, we can accurately analyze what function the jam occurs in and how the resource is occupied. So the problem is how to effectively detect the jamming of Android main thread?

Use ADB system tools to observe the Caton data of app and try to reproduce the scene to locate the problem.

The common way is to use ADB surfaceflinger service and ADB gfxinfo function. In the process of automating the operation of the app, use ADB to obtain data to monitor the fluency of the app, find the time period of occurrence of jamming, and find the scene and operation of occurrence of jamming.

Use the 'ADB shell dumpsyssurfaceflinger' command to obtain the data of the last 127 frames, and calculate the frame rate FPS by periodically executing the ADB command to obtain the number of frames.

Use the 'ADB shell dumpsys gfxinfo' command to obtain the latest 128 frame drawing information, including the time-consuming of the three processes of draw, process and execute for each frame. If the sum of these three times exceeds 16.6ms, it is considered that a jam has occurred.

The existing two schemes are more suitable for measuring the repair effect of regression Caton problem and judging whether there is Caton in some specific scenarios. However, this method has several obvious shortcomings:

With the in-depth study of Android source code, there are two other convenient methods, which are less invasive and occupy less memory, and can be better used in actual scenarios:

The Android main thread updates the UI. If the interface is refreshed less than 60 times in one second, that is, the FPS is less than 60, the user will feel stuck. In short, Android uses @ R_ 403_ 2227 @ update the UI. The UI thread has a looper. In its loop method, it will constantly take out messages and call its bound handler to execute in the UI thread. If there are time-consuming operations in the dispatchmesaage method of the handler, a jam will occur.

Let's look at the source code of loop. Loop ()

Two places marked in red in the code are the log printed before and after the execution of MSG. Target. Dispatchmessage (MSG). By measuring the processing time, we can detect whether some UI threads have time-consuming operations. Note that there are two logging.println functions before and after the execution of this line of code. If logging is set, logs such as "> > > > > dispatching to" and "< < < finished to" will be printed respectively. In this way, we can calculate the execution time of dispatchmessage through the time difference between the two logs, so as to set the threshold to judge whether a jam has occurred.

So how to set logging?

Let's look at the following code:

Looper's mlogging is private and provides a setmessagelogging (@ nullable printer printer) method, so we can implement a printer by ourselves and pass it in through the setmessagelogging () method. The code is as follows:

After setting up logging, the loop method will callback logging.println to print out the time log of each message execution: "" > Dispatching to "and" "Finished to". BlockDetectByPrinter is used to invoke BlockDetectByPrinter.start () in onCreate method of Application.

We can simply implement a logmonitor to record the stack information of the main thread when it is stuck. When > > > > > > dispatching is matched, execute startmonitor, and the task will be executed after 200ms (set Caton threshold). This task is responsible for printing the stack information of UI threads in sub threads (non UI threads). If the message is completed within 200ms, it can be matched to the < < finished log. If removemonitor is executed before the print stack task is started and the task is cancelled, it is considered that there is no jamming; If the message is executed after more than 200ms, it is considered that a jam has occurred, and the stack information of the UI thread is printed.

Here, we use handlerthread to construct a handler. Handlerthread inherits from thread. In fact, it is a thread, but there is one looper more than an ordinary thread. We provide our own getlooper method of this looper object, and then pass in the looper object in handlerthread when creating a handler. In this way, our miohandler object is bound to the non UI thread handlerthread, which handles time-consuming operations and will not block the UI. If the UI thread is blocked for more than 200ms, it will execute mlogrunnable in the child thread and print the current stack information of the UI thread. If the processing message does not exceed 1000ms, it will remove the mlogrunnable task in real time.

When a jam occurs, the general contents of the stack information are printed as follows. The development can locate the time-consuming place through the log.

Advantages: users can monitor the jam situation from the app level when using the app or during testing. Once the jam occurs, they can record the app status and information. As long as the execution of dispatchmesaage takes too long, it will be recorded in metropolis, which will no longer have the problems and shortcomings faced by the first two ADB methods.

Disadvantages: it is necessary to open another sub thread to obtain stack information, which will consume a small amount of system resources.

In the actual implementation, for different mobile phones, different Android systems and even different ROM versions, the loop function may not print logs such as "> > > > > dispatching to" and "< < < finished to", which makes this method impossible.

Optimization strategy: we know that println will be executed at the beginning and end of the loop function, so the optimization version changes Caton's judgment to that when loop outputs the first sentence of log, it is regarded as startmonitor and when loop outputs the next sentence of log, it is regarded as end to solve this problem.

In fact, there is an observer interface in looper that can complete this task well. It is only marked as hide, so we can't use it, but you can know.

The observer interface provides three methods: listening to the start, end and callback of errors.

Link to the official documentation of choreographer.framecallback( https://developer.android.com/reference/android/view/Choreographer.FrameCallback.html )

We know that the Android system sends Vsync signals every 16ms to notify the interface to redraw and render. The period of each synchronization is 16.6ms, representing the refresh rate of one frame. The SDK contains a related class and related callbacks. Theoretically, the time period between two callbacks should be 16ms. If it exceeds 16ms, we think that a jam occurs. Use the time period between two callbacks to judge whether a jam occurs (this scheme is supported only by Android 4.1 API 16 or above).

The principle of this scheme is mainly to set its framecallback function through the choreographer class. When each frame is rendered, it will trigger a callback framecallback, which will callback the void doframe (long frametimenanos) function. One interface rendering will call back the doframe method. If the interval between two doframes is greater than 16.6ms, it indicates that a jam has occurred.

The essence is not much different from log, but this is more general and will not be unavailable due to the model system.

Let's enter the actual combat to see how the code level is implemented.

The mainactivity code is as follows:

The stack information collected is as follows:

For FPS log, you can see the following information:

If you want to use the above methods in your own app, you still need a lot of operations. For details, you can read the references.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>