Android performance optimization (3) - drawing optimization

Although the configuration of the mobile phone running Androidthe mobile phone is constantly improving, it still cannot be PCcompared with the mobile phone, and cannot achieve PCthe large memory and high performance of the mobile phone CPU. Therefore, it is impossible to use unlimited memory and memory when developing Androidapplications CPU. CPUImproper use of memory and memory will also cause problems such as application freeze and memory overflow.

1 Drawing performance analysis

AndroidThe application needs to display its cleansing face to the user, and the user will interact with the cleansing face, so the fluency of the interface is very important.

1.1 Drawing principle

ViewThere are three steps in the drawing process of 3, namely measure, , layoutand draw, which mainly run on the application framework layer of the system, while the actual rendering of data to the screen is done by the services Nativeof the system layer .SurfaceFlinger

The drawing process is mainly performed CPUby the data calculation work of Measure, Layout, Record, responsible for rasterization and rendering. and (graphics processor ) are connected through the graphics driver layer, the graphics driver layer maintains a queue, will be added to the queue, so that the data can be drawn from the queue.ExecuteGPUCPUGPUgraphics processing unitCPUdisplay listGPU

When it comes to drawing performance, we need to mention the concept of frame. The number of frames is 1sthe amount of pictures transmitted in the time, and it can also be understood as how many times the graphics processor can refresh per second, represented by FPS( Frames Per Second). Each frame is actually a still image, and the illusion of motion is created by displaying frames in rapid succession. The simplest example is that when we are playing games, if the screen is at , we 60fpswill not feel stuck, but if it is lower than 60fps, for example 50fps, we will feel stuck. This is because the human brain will continuously receive and process the information seen by the eyeballs. The more frames processed per unit time, the more effectively it can be recognized by the brain. The minimum number of frames that the brain can perceive is loaded. At this time, the 10fps ~ 12fpsbrain It is not clear whether the image is static or changing.

In order to keep the screen at 60fps, the screen needs to 1sbe refreshed 60once within , that is, not 16.6667msrefreshed once (drawing time is 16mswithin ).

Android16msThe system sends out a signal every VSYNCto trigger UIthe rendering of . If every rendering is successful, it can achieve what is required for a smooth picture 60fps, so what is it VSYNC? VSYNCIt is Vertical Synchronizationthe abbreviation of (Vertical Synchronization), which is a kind of timing interrupt. Once VSYNCthe signal is received, CPUit starts to process each frame of data. If an operation costs so much 24msthat the system VSYNCcannot render normally when it gets the signal, frame drops will occur. The user will 32mssee the same frame in the .

There are many reasons for stuttering, the main ones are as follows:

  • layout Layoutis too complex to 16msbe rendered within ;
  • Too many animations are executed at the same time, resulting in CPUor GPUoverloaded;
  • ViewOverdrawing, causing some pixels to be drawn multiple times in the same frame time;
  • UIA slightly time-consuming operation was done in the thread ;
  • GCGCThe pause time is too long or frequently generates a large amount of pause time during recycling ;

1.2 Tools

1.2.1 Profile GPU Rendering

Profile GPU Rendering is Android 4.1a development assistance function provided by the system, which can be turned on in the developer options: Settings –> Developer Options –> GPU Rendering Mode Analysis –> Display as a bar graph on the screen:

GPU mode analysis

The horizontal axis in the figure represents time, and the vertical axis represents the time consumption of a certain frame. The green horizontal line is a warning line. Exceeding this line means that the duration is exceeded. Try to ensure that the 16msvertical color histogram remains below the green line. These vertical colored histograms represent a frame, and the colored histograms of different colors represent different meanings:

  • Orange represents the processing time, which is where CPUtells to GPUrender a frame. This is a blocking call, because CPUit will wait for GPUa reply to the command. If the orange histogram is high, it means that GPUis very busy;
  • Red represents the execution time, which is the time Androidfor 2Drendering Display List. If the red histogram is high, it may be caused by resubmitting the view. There are also complex customizations Viewthat will also cause the red histogram to become higher;
  • Blue represents the time to measure the drawing, that is, how long it takes to create and update Display List. If the blue histogram is high, it may need to be redrawn, or View.onDraw()the method handles too many things;

As the interface is refreshed, the interface will display the rendering time of each frame with a real-time histogram. The higher the histogram, the longer the rendering time. Each histogram has a 16msgreen horizontal line representing the benchmark. The marked column lines contain three parts (blue represents Display Listthe time of measuring drawing, red represents the time required for OpenGLrendering , and yellow represents the time waiting for processing), as long as the total time of each frame is lower than the baseline, there will be no card Ton problem (in fact, it is not a problem if some of them exceed the baseline).Display ListCPUGPUUI

1.2.2 GPUDrawing

For performance optimization, you can also analyze it through the overdraw tool UIin the developer options . GPUAfter turning on debugging in Settings->Developer Options->Debug GPUOverdraw (different devices may have different locations or names), you can see the following picture (analyze the settingscurrent interface overdraw):

GPU drawing

Instructions below:

Overdraw

Blue ( 1xoverdrawn), light green ( 2xoverdrawn), light red ( 3xoverdrawn), dark red (overdrawn 4x) represent 4different levels of Overdrawconditions, our goal is to minimize red Overdrawand see more blue area.

OverdrawSometimes it's because UIof a lot of overlapping parts of the layout, and sometimes it's because of unnecessary overlapping backgrounds. For example, a certain Activityobject has a background, and then the objects inside Layouthave their own background, and at the same time, each child Viewhas its own background. Just by removing non-essential background images, this can reduce a lot of red Overdrawareas and increase the proportion of blue areas. This measure can significantly improve program performance.

If both can be used in the layout RealtiveLayout, LinearLayoutthen use it directly LinearLayout, because Relativelayoutthe layout of is more complicated, and it takes more CPUtime to draw. If multiple LinearLayoutor Framelayoutnested is required, it can be used Relativelayout. Because of the multi-level nesting, most of the drawing of the layout is repeated, which will reduce the performance of the program.

2 Layout optimization tool—Layout Inspector

Debug layouts with the Layout Inspector and Layout Validation tools

3 Layout optimization method

There are many layout optimization methods, mainly including rational use of layout, include, mergeand ViewStub.

3.1 Reasonable use of layout

Commonly used layouts mainly include LinearLayout, RelativeLayoutand , FrameLayoutetc. Reasonable use of them can Androidreduce the workload of drawing and improve performance. for example:

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="布局优化" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Merge" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="ViewStub" />

    </LinearLayout>

</LinearLayout>

You can see that the layout has three layers:

layout hierarchy

The layout has a total of 3layers and contains a total 5of View. If RelativeLayoutrewritten with , the code is as follows:

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="布局优化" />

    <TextView
        android:id="@+id/tv_text2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@+id/tv_text1"
        android:text="Merge" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@+id/tv_text1"
        android:layout_below="@+id/tv_text2"
        android:text="ViewStub" />

</RelativeLayout>

You can see that the layout has two layers:

layout hierarchy

There are 2layers in the layout, and there are a total 4of View, it can be seen from this RelativeLayoutthat the layout of one layer has been reduced. If the layout is complicated, it can be reasonably used RelativeLayoutto reduce the layout level. RelativeLayouthas lower performance than LinearLayout, because the arrangement RelativeLayoutof Viewis based on being dependent on each other.

However, there are many situations faced in the actual development process, and it is not easy to say whose performance is better. In general, it is recommended to use if there are many layout layers, RelativeLayoutand it is recommended to use if there are many nested layouts LinearLayout.

3.2 Use includetags for layout reuse

When multiple layouts need to reuse the same layout, such as one TitleBar, if these cleaning surfaces have to add the same layout TitleBar, it will be very troublesome to maintain, and TitleBarthe layout needs to be copied to each cleaning surface that needs to be added, which is prone to omissions. If you modify TitleBarit, you need to TitleBarmodify it in the referenced layout. In order to solve these problems, you can use includethe label to solve.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="40dp">

    <ImageView
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="center"
        android:src="@drawable/ic_launcher_background"
        android:padding="3dp" />

</LinearLayout>

This consists TitleBarof ImageViewand TextView. Let's TitleBarintroduce into the previously used layout as follows:

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include layout="@layout/title_bar" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/tv_text1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="布局优化" />

        <TextView
            android:id="@+id/tv_text2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_toRightOf="@+id/tv_text1"
            android:text="Merge" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/tv_text2"
            android:layout_marginLeft="10dp"
            android:layout_toRightOf="@+id/tv_text1"
            android:text="ViewStub" />

    </RelativeLayout>
</LinearLayout>

You can see that the layout has two layers:

layout hierarchy

3.3 Use mergethe label to remove redundant layers

mergeIt means merging, and using tags in appropriate scenarios mergecan reduce redundant layers. mergeTags are generally includeused in conjunction with tags. For the example in the previous section, if mergethe tag is used instead LinearLayout, the code is as follows:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="40dp">

    <ImageView
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="center"
        android:padding="3dp"
        android:src="@drawable/ic_launcher_background" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="绘制优化" />

</merge>

Layout hierarchy:

layout hierarchy

It can be seen that the previous LinearLayoutis gone, but there is mergea tag to replace LinearLayoutthe will cause LinearLayoutthe to fail, and the layout will be messed up. mergeIt is best to replace the label FrameLayoutor have the same layout direction LinearLayout. For example, the layout direction of the current parent layout LinearLayoutis vertical, and the layout defense line of the contained child layout LinearLayoutis also vertical, so you can use mergethe label. However, in this scenario the layout direction of TitleBarand layout LinearLayoutis horizontal, which obviously does not meet this requirement.

3.4 Use ViewStubto improve loading speed

A common development scenario is that not all controls on a certain layout are displayed, but a part of them are displayed. In this case, the general method is to use and Viewattributes GONE. VISIBLEThis method is not efficient, although The purpose of hiding is achieved, but they are still in the layout, the system will still parse them, and can be used ViewStubto solve this problem.

ViewStubis lightweight View, invisible and does not take up layout space. When ViewStubcalling inflatethe method or setting is visible, the system will clamp the ViewStubspecified layout, and then add this layout to ViewStub, before ViewStubcalling inflatethe method or setting is visible, it does not occupy the layout space and system resources, its main purpose is to The target view occupies a position. Therefore, using ViewStubcan improve the performance of the cleansing initialization, thus increasing the loading speed of the interface. First, add a tag to the layout ViewStub:

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ViewStub
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout="@layout/title_bar" />

</LinearLayout>

ViewStubUse in the tag android:layoutto refer to the previously written layout title_bar.xml. When running the program, ViewStubthe layout referenced by the label cannot be displayed, because the layout has not been loaded into ViewStub, and then used in the code ViewStub:

public class MainActivity extends AppCompatActivity {
    
    

    private ViewStub viewStub;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        viewStub = findViewById(R.id.view_stub);
        viewStub.inflate(); // 1
        viewStub.setVisibility(View.VISIBLE); // 2
    }
}

The comments 1and comments 2are used to put ViewStubthe layout of the application into the ViewStub, so that the application layout will be displayed. ViewStubPay attention to the following issues when using :

  • ViewStubIt can only be loaded once, and ViewStubthe object will be empty after loading, so ViewStubafter the layout referenced by is loaded, it cannot be used ViewStubto control the referenced layout. Therefore, if a control needs to be continuously displayed and hidden, it is still necessary to use Viewthe Visibilityattribute;
  • ViewStubcannot nest mergetags;
  • ViewStubThe operation is the layout file, if you just want to operate the specific View, you still need to use Viewthe Visibilityattribute;

3.5 Drawing Optimization

Drawing optimization mainly means that View.onDrawthe method needs to avoid performing a large number of operations:

  • onDrawThe method does not need to create a new local object, because onDrawthe method is executed in real time, resulting in a large number of temporary objects, resulting in more memory usage, and the system is constantly running GC, reducing the execution efficiency;
  • onDrawThe method does not need to perform time-consuming operations, and onDrawthe loop is used less in the method, because the loop will take up a CPUlot of time. Leading to unsmooth drawing, stuttering and so on. Google officially pointed out that Viewthe drawing frame rate is stable at 60dps, which requires that the drawing time of each frame should not exceed 16ms( 1000/60). Although it is difficult to guarantee, we need to reduce it as much as possible;

60dpsIt is the most suitable image display speed at present, and it is also Androidthe debugging frequency set by most devices. 16msIf the interface refresh operation is successfully completed, a smooth picture can be displayed, but due to any reason VSYNC, the refresh operation cannot be completed when the signal is received. , frame drops will occur, and the refresh frame rate will naturally drop (assuming that the refresh frame rate 60fpsdrops from normal to low 30fps, the user will obviously feel the freeze).

Guess you like

Origin blog.csdn.net/xingyu19911016/article/details/128815711