Although the configuration of the mobile phone running Android
the mobile phone is constantly improving, it still cannot be PC
compared with the mobile phone, and cannot achieve PC
the large memory and high performance of the mobile phone CPU
. Therefore, it is impossible to use unlimited memory and memory when developing Android
applications CPU
. CPU
Improper use of memory and memory will also cause problems such as application freeze and memory overflow.
1 Drawing performance analysis
Android
The 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
View
There are three steps in the drawing process of 3
, namely measure
, , layout
and 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 Native
of the system layer .SurfaceFlinger
The drawing process is mainly performed CPU
by 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.Execute
GPU
CPU
GPU
graphics processing unit
CPU
display list
GPU
When it comes to drawing performance, we need to mention the concept of frame. The number of frames is 1s
the 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 60fps
will 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 ~ 12fps
brain It is not clear whether the image is static or changing.
In order to keep the screen at 60fps
, the screen needs to 1s
be refreshed 60
once within , that is, not 16.6667ms
refreshed once (drawing time is 16ms
within ).
Android
16ms
The system sends out a signal every VSYNC
to trigger UI
the rendering of . If every rendering is successful, it can achieve what is required for a smooth picture 60fps
, so what is it VSYNC
? VSYNC
It is Vertical Synchronization
the abbreviation of (Vertical Synchronization), which is a kind of timing interrupt. Once VSYNC
the signal is received, CPU
it starts to process each frame of data. If an operation costs so much 24ms
that the system VSYNC
cannot render normally when it gets the signal, frame drops will occur. The user will 32ms
see the same frame in the .
There are many reasons for stuttering, the main ones are as follows:
- layout
Layout
is too complex to16ms
be rendered within ; - Too many animations are executed at the same time, resulting in
CPU
orGPU
overloaded; View
Overdrawing, causing some pixels to be drawn multiple times in the same frame time;UI
A slightly time-consuming operation was done in the thread ;GC
GC
The 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.1
a 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:
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 16ms
vertical 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
CPU
tells toGPU
render a frame. This is a blocking call, becauseCPU
it will wait forGPU
a reply to the command. If the orange histogram is high, it means thatGPU
is very busy; - Red represents the execution time, which is the time
Android
for2D
renderingDisplay List
. If the red histogram is high, it may be caused by resubmitting the view. There are also complex customizationsView
that 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, orView.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 16ms
green horizontal line representing the benchmark. The marked column lines contain three parts (blue represents Display List
the time of measuring drawing, red represents the time required for OpenGL
rendering , 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 List
CPU
GPU
UI
1.2.2 GPU
Drawing
For performance optimization, you can also analyze it through the overdraw tool UI
in the developer options . GPU
After turning on debugging in Settings->Developer Options->Debug GPU
Overdraw (different devices may have different locations or names), you can see the following picture (analyze the settings
current interface overdraw):
Instructions below:
Blue ( 1x
overdrawn), light green ( 2x
overdrawn), light red ( 3x
overdrawn), dark red (overdrawn 4x
) represent 4
different levels of Overdraw
conditions, our goal is to minimize red Overdraw
and see more blue area.
Overdraw
Sometimes it's because UI
of a lot of overlapping parts of the layout, and sometimes it's because of unnecessary overlapping backgrounds. For example, a certain Activity
object has a background, and then the objects inside Layout
have their own background, and at the same time, each child View
has its own background. Just by removing non-essential background images, this can reduce a lot of red Overdraw
areas and increase the proportion of blue areas. This measure can significantly improve program performance.
If both can be used in the layout RealtiveLayout
, LinearLayout
then use it directly LinearLayout
, because Relativelayout
the layout of is more complicated, and it takes more CPU
time to draw. If multiple LinearLayout
or Framelayout
nested 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
, merge
and ViewStub
.
3.1 Reasonable use of layout
Commonly used layouts mainly include LinearLayout
, RelativeLayout
and , FrameLayout
etc. Reasonable use of them can Android
reduce 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:
The layout has a total of 3
layers and contains a total 5
of View
. If RelativeLayout
rewritten 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:
There are 2
layers in the layout, and there are a total 4
of View
, it can be seen from this RelativeLayout
that the layout of one layer has been reduced. If the layout is complicated, it can be reasonably used RelativeLayout
to reduce the layout level. RelativeLayout
has lower performance than LinearLayout
, because the arrangement RelativeLayout
of View
is 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, RelativeLayout
and it is recommended to use if there are many nested layouts LinearLayout
.
3.2 Use include
tags 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 TitleBar
the layout needs to be copied to each cleaning surface that needs to be added, which is prone to omissions. If you modify TitleBar
it, you need to TitleBar
modify it in the referenced layout. In order to solve these problems, you can use include
the 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 TitleBar
of ImageView
and TextView
. Let's TitleBar
introduce 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:
3.3 Use merge
the label to remove redundant layers
merge
It means merging, and using tags in appropriate scenarios merge
can reduce redundant layers. merge
Tags are generally include
used in conjunction with tags. For the example in the previous section, if merge
the 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:
It can be seen that the previous LinearLayout
is gone, but there is merge
a tag to replace LinearLayout
the will cause LinearLayout
the to fail, and the layout will be messed up. merge
It is best to replace the label FrameLayout
or have the same layout direction LinearLayout
. For example, the layout direction of the current parent layout LinearLayout
is vertical, and the layout defense line of the contained child layout LinearLayout
is also vertical, so you can use merge
the label. However, in this scenario the layout direction of TitleBar
and layout LinearLayout
is horizontal, which obviously does not meet this requirement.
3.4 Use ViewStub
to 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 View
attributes GONE
. VISIBLE
This 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 ViewStub
to solve this problem.
ViewStub
is lightweight View
, invisible and does not take up layout space. When ViewStub
calling inflate
the method or setting is visible, the system will clamp the ViewStub
specified layout, and then add this layout to ViewStub
, before ViewStub
calling inflate
the 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 ViewStub
can 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>
ViewStub
Use in the tag android:layout
to refer to the previously written layout title_bar.xml
. When running the program, ViewStub
the 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 1
and comments 2
are used to put ViewStub
the layout of the application into the ViewStub
, so that the application layout will be displayed. ViewStub
Pay attention to the following issues when using :
ViewStub
It can only be loaded once, andViewStub
the object will be empty after loading, soViewStub
after the layout referenced by is loaded, it cannot be usedViewStub
to control the referenced layout. Therefore, if a control needs to be continuously displayed and hidden, it is still necessary to useView
theVisibility
attribute;ViewStub
cannot nestmerge
tags;ViewStub
The operation is the layout file, if you just want to operate the specificView
, you still need to useView
theVisibility
attribute;
3.5 Drawing Optimization
Drawing optimization mainly means that View.onDraw
the method needs to avoid performing a large number of operations:
onDraw
The method does not need to create a new local object, becauseonDraw
the method is executed in real time, resulting in a large number of temporary objects, resulting in more memory usage, and the system is constantly runningGC
, reducing the execution efficiency;onDraw
The method does not need to perform time-consuming operations, andonDraw
the loop is used less in the method, because the loop will take up aCPU
lot of time. Leading to unsmooth drawing, stuttering and so on. Google officially pointed out thatView
the drawing frame rate is stable at60dps
, which requires that the drawing time of each frame should not exceed16ms
(1000/60
). Although it is difficult to guarantee, we need to reduce it as much as possible;
60dps
It is the most suitable image display speed at present, and it is also Android
the debugging frequency set by most devices. 16ms
If 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 60fps
drops from normal to low 30fps
, the user will obviously feel the freeze).