The last article on Fluency concepts described in detail the mechanics of VSync and Choreographer. The content explained may be partial to theoretical concepts, so this paper is the fluency optimization practice. The whole paper is mainly divided into three layers: UI layer, code logic layer and IO layer to describe various optimization points, among which several auxiliary detection plug-ins will be inserted. It is full of dry goods, I hope you are useful.

The most basic UI layer display optimization

  1. Debug GPU overrendering

When there is a lag in the App, we will immediately think about whether our App has the problem of overdrawing. Why should we look at the over-drawing problem first? It is direct, intuitive and convenient. By opening the over-drawing area in the developer option of each mobile phone, we can tell whether our App has over-drawing problem by color. Could there be a part of the test or even development of students who don’t know what overdrawing is? Overdrawing refers to drawing a pixel on the screen multiple times (more than once), such as a TextView with a background, where the pixel displaying the text is drawn at least twice, once for the text and once for the background. The meanings of various colors shown by overdrawing are as follows:

Multiple Overdraw Number of pixels drawn Acceptable area
Colorless, 0X 1 Full screen
blue 1X 2 Most of the
green 2X 3 local
pink 3X 4 A small number of
Deep red 4X 5 or more There is no

Now you can take a look at your project, find an interface that you think is a little bit complicated, and turn on the Developer option to show the overdraw area

By color judgment, we check the corresponding layout code to optimize the overdrawing problem.

  1. Tracer for OpenGL ES

For the over-drawn area we saw above, we need to think about how to optimize it, but at this time we are not quite clear about how this over-drawn area is formed, so we need to use another tool Tracer for OpenGL ES, which can record and analyze the drawing process of every frame of app. As well as listing all drawing functions and time consuming using OpenGL ES, we can easily see how each frame of app is drawn through Tracer for OpenGL ES.

Brief use steps:

  • Connect to the real machine, Open the Android Device Monitor in AndroidStudio, and then Window-> Open Perspective -> Tracer for OpenGL ES.
  • To operate as shown below, click the “Capture track” button, then input the corresponding information and click “track”. (Huawei P10 and MI5 cannot be tracked normally, and Huawei Mate7 is used at last)

  • Click “Stop Tracing” to end and Trace log files will be generated in the predefined directory.

  • When finished, the Trace file opens automatically, as shown in the picture below. Click on the glDraw function bar and see the image drawn by the current drawing function in the upper right.

<? xml version="1.0" encoding="utf-8"? > <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white">

    <Button
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@android:color/background_dark"
        android:text="Button"
        android:textColor="@android:color/white" />

</android.support.design.widget.CoordinatorLayout>

Copy the code

From this demonstration, we can use Tracer for OpenGL ES to find overdrawn areas.

Episode: Open your MAC AndroidStudio3.1, actually can not find DDMS, access to the data found that DDMS in AndroidStudio3.1 has not recommended use, can only be used back to the company AndroidStudio3.0 screenshots, here for the future optimization tool article left a hint.

  1. Hierarchy Viewer

Next, we introduce the Hierarchy Viewer, through which we can find the unreasonable layout. The Hierarchy Viewer is easy to use. In AndroidStudio, the Android Device Monitor is also used. Then Window-> Open Perspective -> Hierarchy Viewer. The Hierarchy Viewer shows the UI Tree of the Activity we opened. (Note: We used the emulator as an example. Run your app in the emulator and then open the Hierarchy Viewer panel.)

After getting the UI Tree, we mainly analyze the following three problems :(I use a simple Demo to analyze)

Use the Hierarchy Viewer to View our UI Tree. If you find that the red-box RelativeLayout is the only child of CustomTestView, We can see if we can put the RelativeLayout sub-View back into the CustomTestView so that we can get rid of the RelativeLayout layer, and we can look at the code and see that the RelativeLayout layer is actually redundant, We merge RelativeLayout and CustomTestView directly with the Merge tag (this is very common with custom views).

【 Question 2】 The UI is set to view. GONE in some cases. We often encounter this situation when developing applications, and dynamically decide which View or ViewGroup to display at runtime based on conditions. I’m going to display the first one, not the first one. I’m going to set it to view.gone. The advantage of this is that the logic is simple and it is very easy to control, but the downside is that it consumes resources. Even though the View or ViewGroup’s initial View is set to View.gone, when the View is Inflate, the View is still Inflate, which means that objects are still created and instantiated. Property will be set, that is, will consume memory and other resources. The official recommendation is to use a ViewStub, which is a lightweight View that uses very little resources. In our code, the error page ErrorView often appears in this case.

The layout is nested with two linearLayouts, but we can achieve the same effect by using a RelativeLayout, which reduces one layer. The optimization effect can be clearly seen in the figure.

  1. Remove or modify Windows’ default Background. We usually use a default Background color when setting a common Theme as the base color of the application
    <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/app_status_bar_bg_color</item>
        <item name="colorPrimaryDark">@color/app_status_bar_bg_color</item>
        <item name="android:textColorPrimary">@android:color/black</item>
        <item name="android:windowBackground">@color/app_frame_bg_color</item>
    </style>
Copy the code

But in the layout page, the designer’s background color is not the default background color at all. If we set another background in the root layout of the page, we will draw another background.

<? 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"
    android:background="@android:color/white"> <! RelativeLayout </RelativeLayout>Copy the code

In this case, we can deal with it as follows:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setBackgroundDrawableResource(android.R.color.white);
        setContentView(R.layout.activity_main); * * *}Copy the code

By changing the background color of the layout this way, we can avoid overdrawing. In addition, the above setting background code, to pay attention to the order of writing, here can contain a lot of View to create knowledge, interested students can consult.

  1. Reduce writing views and viewgroups
  • You can use a RelativeLayout to reduce the hierarchy, use a RelativeLayout, otherwise use a LinearLayout. Because Android has more measurements with a RelativeLayout than a LinearLayout without weight, ViewGroup with RelativeLayout, LinearLayout, and FrameLayout.
  • Use SpannableString. I’m sure you’re all familiar with SpannableString, which is a great tool to optimize and reduce writing views.
  • Elegantly set sectionals for LinearLayout and RecyclerView.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:divider="@drawable/divider_horizontal_w7_transparent"
    android:orientation="horizontal"
    android:showDividers="middle">
    ***
</LinearLayout>
Copy the code
mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDraw(c, parent, state); }});Copy the code

It is strongly not recommended to draw a line in the itemView directly, although it is simple, but we are ambitious developers, try to write beautiful code.

  • Use merge tags, ViewStub tags, and include tags. We talked about all of that.
  • DrawableLeft instead of ImageView + TextView
  • Use ConstraintLayout. The recommended default layout for the new version of AndroidStudio, and to its great advantage, it’s an enhanced version of RelativeLayout, a Deprecated replacement for the percentage layout.
  • %1$d replaces TextView + TextView. (You understand the importance of this if you need multilingual adaptation)

Here to help you sort out a few classic attention points, because there are different levels of readers, so there is no specific code to explain, if you do not understand, you can refer to a point alone.

  1. Lint Lint Lint Lint Lint Lint Lint Lint Lint Lint Lint Lint Lint Lint Lint Lint Lint Lint Lint Lint And then he gave up. This next one is really good for this section. Step to open Lint: Analyze -> Inspect Code -> Select the directory you want to Analyze and click OK to Analyze

Under the Android Lint: Performance error node, there is a very clear description of what errors you have, and how to correct each error. The arrow on the right of the application helps us directly locate the error code, is very convenient!

Code logic layer optimization

After the above analysis, let’s move on to the optimization of the code logic layer.

  1. Traceview

Traceview is a very useful performance analysis tool for Android devices. With a detailed interface, it allows us to track the performance of programs and clearly view the time and call times of each function. Therefore, when we use Traceview, there are two main reasons affecting the smoothness. One: the main thread occupies a long CPU time method function; Two: the number of times the thread calls

I made a specific analysis through specific applications, such as the home page of mall type. By using RecyclerView, we can infer that the fluency of RecyclerView is mainly influenced by RecyclerView. Adaptor #onBindViewHolder.

Again using the Android Device Monitor panel, select the application you want to analyze on the left side of the image below and click the button in the upper left corner. When you feel that you have collected enough data, click the button again and Traceview will automatically open the Trace file.

The upper part of the Traceview panel is divided into the time line panel. The upper left panel shows the thread information collected in the collection data, while the upper right panel shows the time line and the function call information involved in each thread during the collection period. And the lower panel is divided into function analysis, is the core traceview interface, it provides information data is very much, he showed the main method of every function in a particular thread calls, including CPU time, function method call number, real information such as the execution time and function method, the information is our analysis of the key to fluency.

Let’s look at the operations and get the order in which the methods are called:

  • Search for the method name of the response in traceView
  • The search results are automatically expanded to include information about Parents and Children
  • Click the name of the method under Parents to jump directly to calling the current method. The Children, on the other hand

Profile Panel Function description of each column

The column name describe
Name The name of the function method called
Incl Cpu Time The CPU time used by a function, including internal calls to other functions
Excl Cpu Time CPU time used by functions, but not internal calls to other functions
Incl Real Time The real time (in milliseconds) that a function runs, including the real time spent calling other functions
Excl Real Time The real time in milliseconds for a function to run, excluding the real time spent calling other functions
Call+Recur Calls/Total The number of times a function is called, recursive calls as a percentage of total calls
Cpu Time/Call The ratio of the CPU time of a function call to the number of calls is the average execution time of the function
Real Time/Call The ratio of the CPU time of a function call to the number of calls is equal to the average execution time of the function, which includes the execution time of other functions called internally

I found a ViewHolder on the home page by searching for the abstract method inflateFromModel called in recyclerView.adapter #onBindViewHolder. So we can see from this ViewHolder#inflateFromModel method that it calls two methods, one is the image display method and the other is the regular judgment method, and since ViewHolder#inflateFromModel is constantly being called in a sliding mechanism, View#setTag, onClick(), onClick(); This reduces method functions that don’t need to consume CPU time on the main thread, resulting in improved fluency.

  1. Systrace

Systrace visually shows the order and elapsed time of the API calls on each thread. Using the Android Device Monitor panel, the arrow in the following figure suggests that the tracking duration should not be too long to better locate problems. The trace.html file is generated and opened in Google Chrome.

Let’s start with a few common shortcuts:

operation role
w amplification
s narrow
a Shift to the left
d Moves to the right
m Marks the currently selected region
/ Search keywords

The normal drawing is 60 frames per second, which is about 16.6 milliseconds per frame. Below this value, the normal color is green. If it exceeds this value, it will turn red and yellow. Anything that’s not green means something’s wrong. In this case, you need to use the ‘W’ key to enlarge the frame, and then press the ‘M’ key to highlight, to further analyze the problem.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Trace.beginSection("Activity_onCreate");
    ***
    Trace.endSection();
    }
Copy the code

Overall, Systrace presents a lot of information, but how to make use of it still needs to be studied. The location of the code mentioned above looks like a Log, monitoring the start and end times, which feels a bit weak.

App IO layer code optimization

IO can be divided into network request and disk read and write IO. I believe we all know that the Model layer in MVP mode also operates on the IO layer. Long time and frequent IO operations in the main thread have a great impact on the smoothness. After Android 4.0, network operations in the main thread are no longer possible, otherwise the program will crash. Therefore, we need to monitor the IO layer operations. Android provides StrictMode to monitor code for this.

StrictMode mainly has two types of policies: TreadPolicy and VmPolicy.

  1. Thread policies are mainly used to detect whether there are disk reads and writes in the UI thread, network requests, and custom code execution is slow in the UI thread
  • Custom time-consuming calls are enabled with detectCustomSlowCalls()
  • Disk reads are enabled with detectDiskReads()
  • DetectDiskWrites () is enabled
  • Network operation is enabled with detectNetwork()
  1. VmPolicy is used to detect memory problems, such as Activity memory leaks, SQL object memory leaks, and I/O operation object resources that are not released.
  • ActivityLeaks Start with DetectacvityLeaks ()
  • Not closed Closable object leaks using detectLeakedClosableObjects () work
  • Leaked Sqlite object using detectLeakedSqlLiteObjects () work
  • Checking the number of instances is enabled using setClassInstanceLimit()

As long as the main thread is configured and started, it listens to the main thread and notifies the user in the form of logcat when it detects major problems and policy violations.

Summary of fluency optimization experience

Finally, LET me summarize the experience of fluency optimization throughout the article:

  1. UI Layout optimization
  • Use a LinearLayout instead of a RelativeLayout because the LinearLayout performs slightly better
  • For complex layouts, we can use RelativeLayout to solve complex layout relationships
  • Minimize the use of the Layout_weight attribute of the LinearLayout because it consumes a lot of performance
  • Use include tags to reuse layouts that can be reused
  • Use the ViewStub tag to load layouts that are not necessarily used
  • Use merge to reduce unnecessary level nesting
  • Remove redundant background colors to reduce overdrawing problems
  • Use compound drawables, %1$d to reduce layout creation

2.RecyclerView performance optimization

  • Reuse problem in recyclerView.adapter #onBindViewHolder function, note which unnecessary variables are created
  • Asynchronously loading images
  • For some unnecessary operations do not implement in the sliding multiplexing section, this will affect CPU calculation
  1. UI is the main thread
  • Request network data asynchronously
  • Do not implement time-consuming operations in the UI thread
  • Do not manipulate the UI outside the UI thread

4. Third-party platforms

  • Tencent open source tool — GT
  • Listen cloud – Application performance monitoring platform

At the end of this blog, the canary version of AndroidStudio3.2 has just appeared, and some of the above tools are no longer recommended by Google. Next, I will continue to update the article of optimization tools recommended by Google, and strive to become a good hand of performance optimization.