Layout optimization? This is how it should be played
Layout optimization is an important part of Android performance optimization. So, what should we do during development to help us develop smooth Android apps? When the layout is stuck, how to analyze the location of the problem? This article will teach you how to play layout optimization step by step, from principle to practice.
concept
Reasonable layout can effectively improve performance, speed up the page display speed, simplify the complexity of logic. The impact of layout on Android performance mainly includes two aspects: measurement + drawing.
role
Through layout optimization, effectively reduce page lag, frame loss and other situations, to achieve smooth application.
Basic knowledge of
Why is it easy to get stuck when the layout is complex? What is the hardware acceleration added after 5.0 and why does it improve fluency? I wrote an XML file and it shows up on my phone screen. In order to understand these are related to THE KNOWLEDGE of UI layout optimization, then we have to talk about some basic knowledge of layout optimization. It may be boring, but I believe that when you read it, you will have a clear answer to the above questions.
GPU VS CPU
What do we mean by CPU and GPU? While talking about this, we have to talk about rasterization.
Now take a screenshot of your phone, and there’s an @ sign in it, and zoom in, zoom in, zoom in
See, every piece of data (picture, text, letter, number, etc.) displayed on our screen is made up of little squares, just like the @ in our GIFs. This is called rasterization. This rasterization of text is time-consuming and laborious. So The Android system assigns this function to the GPU.
The CPU is only responsible for texturing the buttons in the XML layout, and the GPU is responsible for rasterizing the texture and rendering it.
Open GL
GPU is a separate hardware, and there are so many GPU models on the market. When the CPU transfers data to the GPU, it must comply with certain interface rules. Open GL.
Open GL is a professional graphical programming interface specification across programming languages and platforms. The texture data transmission between CPU and GPU is realized through Open GL interface.
Rendering principle
Now that we know what the CPU and GPU do, how does the texture data we give to the GPU show up on our display? This is where Android rendering comes in.
Graphic consumer – display
The contents of the display are read from the hardware frame buffer. Starting at the starting position of the Buffer, the entire Buffer is scanned, and the contents are mapped to the display.
This contains two buffers:
Front buffer: Used to display content into the screen frame buffer.
Backbuffer: The frame buffer used to compose the next frame in the background.
Obviously, switching roles between the two buffers can effectively speed up the display of graphics.
Graphic producer
Since the display can take data from the buffer and display it, there must be a module that produces the data.
To better understand the functions of this section, let’s take a look at the commonly used wechat pages and break them down.
Surface
Our application can display multiple layers, each layer is actually a Surface, it is like a drawing board. View’s onDraw method draws the corresponding layout into the Surface, and each layer of data corresponds to a Surface. You can interpret the Surface as a description of the Android window. An application can contain up to 31 Windows.
GraphicBuffer
There are multiple buffers inside the Surface, forming a BufferQueue, which is represented by GraphicBuffer. J just as Surface is the description of an Android window, GraphicBuffer is the description of an Android GraphicBuffer.
SurfaceFlinger- Graphic compositor
What wechat finally shows in front of us is definitely like the figure above. Then how to synthesize the multi-layer data of the Surface and form the data required by the corresponding screen cache? That’s the main feature of SurfaceFlinger. It will synthesize all the graphics in the Surface corresponding to the upper layer. Of course, this is done in part through operations on the BufferQueue.
Let’s take a look at the drawing process from a Buffer flow:
- The Buffer is moved from the Surface BufferQueue to the upper layer (the producer of the graph, which you can understand as the Canvas drawn by our XML file?). .
- The Buffer is added to the BufferQueue after the upper layer draws.
- SurfaceFlinger takes the Buffer and composits it (the compositing data is put into the back Buffer of the display).
- Put the BufferQueue back when the composition is done.
Through circulation, the process of Buffer being recycled is realized. Thus a rendering process is implemented.
The refresh mechanism
The Android system signals VSYNC every 16ms to trigger a rendering of the UI, which, if successful each time, should achieve the 60FPS required for smooth graphics.
If we want to achieve smooth graphics, we must ensure that the entire rendering process is completed within 16ms. If the page is more complex, led to onLayout, ontouch special long or complex processing main thread more time lead to 16 ms cannot buffer after the completion of preparatory work, then the current can’t achieve before and after the exchange of the cache area, leading to the page still shows the previous data, give a person a kind of feeling of caton.
Let’s look at a specific process of carton.
Let’s look at the exceptions that will occur in chronological order:
Step1. Display displays the data of frame 0. At this time, the CPU and GPU render the picture of frame 1 and finish it before the next frame displayed by Display
Step2. Due to timely rendering, the Display will normally Display the first frame after the Display of frame 0 is completed, that is, after the first VSync
Step3. For some reason, such as CPU resource is occupied, the system does not start processing frame 2 in time until the second VSync is about to arrive
Step4. When the second VSync comes, the second frame is still displayed because the data is not ready for frame 2. This situation has been named “Jank” by the Android development team.
Step5. When frame 2 data is ready, it will not be displayed immediately, but wait for the next VSync.
Optimization tools
There are a number of causes for UI stagnation. For example, the page is complex, the hierarchy is deep, the Bitmap is too large, some operations occupy the UI thread, and so on. It would be nice if we could identify the problem empirically. But in many cases, we can’t know what causes the jam, so we need to use tools to help.
Systrace
introduce
Systrace can effectively track system I/O operations, kernel work queues, CPU load, and the health of Android subsystems, and then generate HTML reports that can be used to analyze our lag and rendering problems.
use
- Way of DDMS.
DDMS has been removed from Android Studio3.0 and needs to be started by SDK \tools\monitor.
- Python method
For DDMS, we need to start our program first. If the first page of our app starts to lag, it will be a test of speed. Start, DDMS quickly selects the application, and then captures the data. You’ve been single for 20 years, and your hands are fast.
At this point we can use Pythoy. This method requires a Python environment installed. The execution instruction is
python systrace.py -b 32768 -t 5 -a com.***.** -o browser.html sched gfx view wm am app
Copy the code
When the page displays Starting Tracing, the page is ready to operate. In this case, we set the collection to 5 seconds.
- Code way
Systrace has no way to control the start and end of Trace. If we determine that we want to analyze a section of code, we must specify a specific Label for that code so that our trace information can be found in the view generated by Systrace. This is done in code mode, at the start of the code to be monitored and the result is handled by adding monitoring code.
TraceCompat.beginSection("Fragement_onCreateView");
// Own code snippet
TraceCompat.endSection();
Copy the code
The result of the Trace will contain information about the time period during which the Fragement_onCreateView process was run (you must turn on the -a option!).
It is possible to generate concrete HTML data in all three ways. Open it through the browser.
Results analysis
After opening it in a browser, find it in Frames on the left. You can see the red, pink and green circles on the right. Generally, red represents the areas that have problems and need to be optimized.
When we click the red button, there will be alerts. These alerts can be used to locate the problem method. However, if you want to know more specific information, you need to use Traceview to locate and solve problems. In actual combat
This figure shows the information of systrace, a code with a problem. As you can see, the alert in this part is Expensive measure/layout pass. The measure or layout time is too long. RV OnLayout takes a long time. OnCreateView and onBindView occupy most of the time. At this time we can know that RecyclerView layout is a problem. So just look at the layout and the code in the onBindView.
Common mistakes
- Scheduling delay: Rendering a frame is delayed by a few milliseconds, resulting in a nonconformity. Ensure that code on the UI thread is not blocked by work done on other threads, and that background threads (for example, network or bitmap loading) are running or lower in Android.os.process # THREAD_PRIORITY_BACKGROUND, so they are less likely to interrupt the UI thread.
- Expensive measure/layout pass: Measuring /layout takes a long time and causes frames to drop, so avoid triggering relayouts during animation.
- Long View#draw() : Logging invalid drawing commands takes a Long time. Avoid time-consuming operations, especially Bitmap allocation and drawing, when customizing views from views or Drawable.
- Expensive Bitmap uploads: Modified or newly created Bitmap views need to be sent to the GPU, which can be time-consuming if the total number of pixels is large. So minimize the number of Bitmap changes per frame.
- Old-style View Alpha Usage: Set alpha to translucent value
Systrace can intuitively represent the situation and cause of frame loss, but it is powerless to handle specific functions (i.e. code level). Sometimes, TraceView is needed to locate and analyze problems.
Layout Inspector
introduce
The Layout Inspector is a View Hierarchy analysis tool that comes with Android Studio (Android Studio2.2 and later). It replaces the Hierarchy View tool that is often used in Android Studio. It allows the view hierarchy of an application to be examined at run time.
Method of use
After the application launches, go directly to the Tools->Layout Inspector in Android Studio. Launch the Layout Inspector. (This version is Android Studio4.0. The old version is Tools->Android->Layout Inspector).
When opened, you can see the current page of the phone, with the layout hierarchy on the left. On the right are the properties of the selected control.
With this feature, you can analyze the hierarchy of the layout, reduce unnecessary layers, reduce overdraw problems, and achieve optimization of the rendering.
TraceView
Introduction to the
TraceView is a visual tool that allows you to see some specific information about the code at runtime, the duration of method calls, the number of times, the time ratio, and so on. Understand the efficiency issues in the process of code execution, so as to improve the code accordingly. For problems that cannot be accurately located in Systrace, TraceView can accurately display them.
Method of use
- Way of DDMS.
DDMS has been removed from Android Studio3.0 and needs to be started by SDK \tools\monitor.
Click the operation page after startup, and then click again to stop the collection of information.
- Code method
The start and result of the code that needs to be monitored is handled by adding monitoring code.
Debug.startMethodTracing("Fragement_onCreateView");
// Own code snippet
Debug.stopMethodTracing()
Copy the code
When you run this code, a trace file is generated. File in sdcard->Android->data-> package name -> Files. The file is then exported and can be viewed through DDMS.
The first is relatively simple, but the scope of the test is broader. The second is more precise and applies when you have a general idea of the specific problem point.
Results analysis
When the corresponding trace is opened through DDMS, the following page is displayed:
The key indicators in the second half are of major concern. Average CPU time and number of calls and recursions are two commonly used metrics.
The name on the left represents the method executed.
If I open it, I divide the method into two parts.
Location problem
Which methods take a long time to implement
Click ON Cpu Time/Call in TraceView to sort Cpu usage from highest to lowest
Which methods are called very frequently
Click on Calls + Recur Calls/Total in TraceView and order the Calls from highest to lowest
After sorting, check whether there is project code or dependent library code one by one. If there is, click to view details to see if there is a problem with this method or the submethod called to further locate the problem.
Practical analysis
The following example adds Debug code to monitor a method in a project after it is found to be time-consuming.
We first sorted by Cpu Time/Call, then searched the left side of the code file we had written, and found that the BaseRectckerViewAdapter method was time-consuming, and then by looking for the Childen that was time-consuming, layer by layer, It turns out that the getItemView method, which basically loads the layout file through the Inflate method, is time-consuming. Locating the layout file is problematic, resulting in slow parsing. Then use the Layout Inspector to view the hierarchy and optimize the Layout to solve the problem.
Monitoring in advance
Since layout is often time-consuming, is there a way to get the layout time-consuming solution? The answer is yes, and there is more than one way
Methods add time before and after
This method, I believe you should have used. Monitor the layout for problems by adding code before and after the setContentView() method and counting its time. Regardless of whether you are an industrious CVER or not, this method is definitely tired to write. It’s fine when it’s small, but once the page is very large, it’s estimated that cu will die
AOP aspect programming
Faceted programming, which is a well-worn technique in background development, is relatively rare on Android. Since it is faceted programming, it is necessary to have a pointcut that can be woven into our own code. So the place to start here is the setContentView method. Use @around for section processing
@Around("execution(* android.app.Activity.setContentView(..) )")
public void getSetContentViewTime(ProceedingJoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
String name = signature.toShortString();
long time = System.currentTimeMillis();
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
LogHelper.i(name + " cost " + (System.currentTimeMillis() - time));
}
Copy the code
Reload the app, then open a few pages, and you can see the corresponding page layout loading log information
Kailaisi - LOG: │ [PerformanceAop. Java |36 | getSetContentViewTime] AppCompatActivity.setContentView(..) cost 174
Kailaisi - LOG: │ [PerformanceAop. Java |36 | getSetContentViewTime] AppCompatActivity.setContentView(..) cost 13
Kailaisi - LOG: │ [PerformanceAop. Java |36 | getSetContentViewTime] AppCompatActivity.setContentView(..) cost 44
Kailaisi - LOG: │ [PerformanceAop. Java |36 | getSetContentViewTime] AppCompatActivity.setContentView(..) cost 61
Kailaisi - LOG: │ [PerformanceAop. Java |36 | getSetContentViewTime] AppCompatActivity.setContentView(..) cost 22
Copy the code
ARTHook scheme
ARTHook hooks corresponding methods and then performs its own logical processing before or after the method call, using the Epic open source library. Specific usage and implementation principle, we can go to read.
Hook all the constructors in the onCreate method in the Application, and then hook them into the setContentView method.
DexposedBridge.hookAllConstructors(AppCompatActivity.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Activity object = (Activity) param.thisObject;
Class<? extends Activity> aClass = object.getClass();
DexposedBridge.findAndHookMethod(AppCompatActivity.class, "setContentView".int.class, new LayoutTimeMethodHook());
}
});
Copy the code
The LayoutTimeMethodHook implements the corresponding timing function.
/ * *
* Description: The time used to Hook setContentView
*
* Author: Kailaisi
* <br></br> Create time: 2020/6/16:22:43
* /
class LayoutTimeMethodHook : XC_MethodHook(a){
var start: Long = 0
@Throws(Throwable::class)
override fun beforeHookedMethod(param: MethodHookParam) {
start = System.currentTimeMillis()
val thisObject = param.thisObject as Activity
Log.d(TAG, thisObject.javaClass.simpleName + "beforeHookedMethod: startTime+" + start)
}
@Throws(Throwable::class)
override fun afterHookedMethod(param: MethodHookParam) {
super.afterHookedMethod(param)
val thisObject = param.thisObject as Activity
val name = param.method.name
// Implement our own logic
Log.d(TAG, thisObject.javaClass.simpleName + "afterHookedMethod: endTime:" + System.currentTimeMillis())
Log.d(TAG, "${thisObject. JavaClass. SimpleName} : ${name} take: a total of ${system.currenttimemillis () - start}")
}
companion object {
private const val TAG = "LayoutTimeMethodHook"
}
}
Copy the code
When you run the above code, you can print out the time consuming information
2020-06-21 16:34:23.541 2851-2851/com.kailaisii.cll D/LayoutTimeMethodHook: SplashActivity :setContentView102
2020-06-21 16:34:24.692 2851-2851/com.kailaisii.cll D/LayoutTimeMethodHook: SettingActivity :setContentView15
2020-06-21 16:34:32.540 2851-2851/com.kailaisii.cll D/LayoutTimeMethodHook: LoginActivity :setContentView47
Copy the code
As you can see, you can also count the elapsed time of each Activity’s setContentView.
Layout optimization scheme
The best way, of course, is to nip it in the bud. Problems to time-consuming and laborious search, certainly not in the development of the time to pay attention to certain layout optimization methods, once and for all.
Load optimization
Our previous explanation of the Inflate source code for Android results in the concrete inflate process. Knowing the whole process of inflater, it mainly includes the following two points:
- XmlPullParser is an IO operation, and the more complex the layout, the longer the IO takes.
- CreateView is created by reflection for each View, and the more controls there are, the longer the reflection takes.
So for the optimization of loading, we can start from these two aspects.
- AsyncLayoutInflater, passes the inflater to a child thread.
- X2C: Avoids IO and reflection by dynamically loading the view.
Draw the optimization
The deeper the hierarchy is, the more complex the layout is, the more time will be consumed when executing onLayout and onDraw. Therefore, the optimization effect can be achieved by reducing the rendering tasks of GPU through certain level optimization.
Layout hierarchy and complexity optimization
Mearsure, layout and draw are traversed from top to bottom. The deeper the hierarchy, the more time it takes to traverse. RealtiveLayout nesting can cause onMeasure to fire multiple times, etc.
Optimization idea
- Reduce the View tree hierarchy.
- The layout should be wide and shallow, avoid narrow and deep.
- ConstraintLayout is a flat layout, and use it whenever possible.
- Avoid nesting with RelativeLayout
- No longer use weight in the LinearLayout
- You can use the merge tag to reduce the level of the root View.
- The ViewStub is lazy loading.
Avoid overdrawing
A pixel is best drawn only once, drawing more than once is a waste of GPU resources.
Optimization idea
- Avoid hierarchies
- Get rid of the extra backgroud
other
- Avoid too many animations at the same time.
- Custom views avoid triggering onMeasure, onDraw, and onLayout frequently.
- Remove redundant resources and logic to avoid consuming CPU slices of the main thread.
conclusion
This article starts from the principle of drawing, and then introduces some commonly used optimization tools and optimization programs. Hopefully this article will give you a deeper understanding of layout optimization and how it works.
reference
https://mp.weixin.qq.com/s/VdG3bqjYjDvd285OIcffBA
https://blog.csdn.net/u012267215/article/details/86013223
https://www.jianshu.com/p/1a063c9073b1
https://blog.csdn.net/haigand/article/details/90584757
https://www.jianshu.com/p/6bce4e256381
http://bxbxbai.github.io/2014/10/25/use-trace-view/
https://www.jianshu.com/p/1b64024f2d08
https://mp.weixin.qq.com/s/2wxLTb8gaYg5aRw3QdVAdA
https://blog.csdn.net/u012267215/article/details/86013223
https://blog.csdn.net/carson_ho/article/details/79549417)
https://www.baidu.com/link?url=Y5v5WLhIwXiNEGVGY5dYSKF3fojrb806fHnEDnNw-aGBX8Nv2vXxVoRchaD4UcUF&wd=&eqid=832d3037000dd3d e000000035eef2549
This article is published by Kaiken!
Synchronous public number [open Ken]