Performance optimization is a platitude, but also an important point. Having done a bit of performance optimization, I now summarize the optimization points in the work. Please correct me if there are any mistakes.

What aspects need to be optimized

In the usual optimization process, which points do we need to optimize from? In fact, we must have used software at ordinary times, in the process of using software, what do you want to ridicule it?

“This app is not finished downloading!” “, “Too slow! “, “Why hasn’t the image been loaded yet!” “, “how just get into the card!” “, “So I quit!” Wait, is that a thought? These ideas actually cover what we are going to talk about today, which aspects to optimize our APP, and I summarize the following points.

  • APK slimming optimization
  • Start speed optimization
  • Stability optimization
  • Memory optimization
  • Operation fluency optimization

Of course, the need to optimize is not only these aspects, I do more optimization is these aspects, for the time being, these aspects to talk about optimization.

APK thin body

How do I view the composition of APK

If we want to optimize the size of APK, we first need to know what is included in the compiled APK, and then we can reduce the size of the APK by cutting out the ones that are too large, or removing the ones that are not needed.

Check the content usage of APK is very simple. Open AS and drag APK into AS to check the content contained in APK.

You can see that the majority of the code is RES code and so on, so slimming can be considered from these aspects.

How do I reduce the RES resource size

  1. Delete redundant resources

As the project goes through iterations, some images and other resources will no longer be used, but they may still be compiled into APK, so you can remove these unused resources. You can use lint to search for images and other resources that are no longer used in the project.

  1. Optimization of duplicate resources

In addition to redundant resources, there are also some images with different file names, but the same content, you can compare the MD5 value to determine whether the same resources, and then edit resources. Arsc to redirect.

  1. Image compression

Uncompressed image files take up a lot of space. You can compress uncompressed images to slim them down. A common tool is the Tinypng website.

You can also use plug-ins such as the TinyPngPlugin or other open source tools to help compress images.

  1. Resources to confuse

Reduce the size of APK by changing the resource path RES /drawable/wechat to R/D/A. When APK has many resource items, the effect is more obvious. This is a wechat open source tool with the detailed address: AndResGuard

  1. Specify the language

If there is no special requirement, only Chinese can be compiled, because other languages are not needed, if the language is not used, it will take up a lot of space in the resource table, so

android { defaultConfig { ... // Only Chinese resConfigs "zh"}}Copy the code

How to reduce the size of so library resources

  1. Self-compiled SO

Remove debug symbols from so in the release package. You can use the ARM-Eabi-strip tool provided with the Android NDK to remove unnecessary debug symbols from the native library.

If cmake is used to compile, you can edit the script to add the following code

set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
Copy the code
  1. Someone else compiled so

Contact the author to modify, generally difficult to contact.

  1. Dynamic delivery of SO

You can deliver SO through the server, and then enter the application after downloading, but the experience is not good, but it is a concept.

  1. Only so for a given platform is compiled

Generally, we are developing for the ARM platform. If there is no special situation, we usually only need to consider the ARM platform. To do this, add the following code to build.gradle in app

android {
    defaultConfig {
        ndk {
            abiFilter "armeabi"
        }
    }
}
Copy the code

The platform differences are as follows:

platform instructions
armeabi-v7a Arm 7th generation and above processors, devices after 2011 are basically all
arm64-v8a Arm eighth-generation 64-bit processor devices
armeabi Arm 5th and 6th generation processors, early machines were all on this platform
x86 X86 32-bit platforms, tablets and emulators
x86_64 X86 64-bit platform

How can I reduce code resource size

  1. Try to use one library for each function

Do not mix Glide and Fresco, for example, to load a photo library. The functionality is similar, but the method is different. If you use multiple libraries to do the same thing, you will have more code.

  1. confusion

Confusion reduces the size of the generated class, which in turn reduces the size of the APK by a certain degree.

  1. R File inline

Reduce the size of R files by inlining resources in R files to code.

You can inline r files using the shrink-r-plugin tool

Reference documentation

Android App package slimming optimization practice

startup

Startup type

Generally divided into cold start and hot start

Cold startup: The system needs to create a new process and start the application based on startup parameters.

Hot start: The application is started when the system already runs the application process (for example, press the home button to temporarily exit the application).

How do I get the startup time

  1. The adb command

Adb shell am start-s -w Adb shell am start-s -w

adb shell am start -S -W xxx/xxxActivity
Stopping: xxx
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=xxx/xxxActivity }
Status: ok
Activity: xxx/xxxActivity
ThisTime: 770
TotalTime: 770
WaitTime: 848
Complete
Copy the code

ThisTime: indicates the last time the Activity was started

TotalTime: Indicates the startup time of all activities during startup

WaitTime: indicates the creation time of the application process + TotalTime

Usually we just focus on TotalTime.

In addition, Google also provides a measurement method in Android4.4 (API 19). You can also see the startup time by filtering the Displayed field in logcat

Displayed I/ActivityManager: Displayed XXX /xxxActivity: +623ms

+623ms is the start time of the Activity.

  1. The time stamp

The timestamp method is based on the following two points.

  • As soon as the Application process is created, it calls Application’s onCreate method.
  • The onWindowsFocusChange method is called after the onResume() method when you first enter an Activity.

Combining these two features, we can obtain the cold start time of the Application by using the timestamp in the onCreate() method of an Application and the onWindowsFocusChange method of an Activity.

How do I monitor the startup process

  1. systrace

Systrace is a powerful tool that can be used to check for stalling problems as well as application startup problems. The following is an example:

Python $ANDROID_HOME/platform – the tools/systrace/systrace. Py GFX view wm am PM ss dalvik app sched – your package name b 90960 – a – o test.log.html

Open test.log.html in Google Chrome to see detailed startup information.

  1. The Debug interface
package android.os; . class Debug { ... public static void startMethodTracingSampling(String tracePath, int bufferSize, int intervalUs) { } public static void startMethodTracing(String tracePath, int bufferSize) { } }Copy the code

Using these two methods of the Debug class, you can generate a trace file that can be opened directly in AS. Can see from the startMethodTracingSampling startMethodTracing method call in the process of the information, also can better analysis startup problem.

There are generally those optimization methods

  1. Time-consuming operations are placed in an asynchronous process

For example, a new thread can be created to perform time-consuming I/O operations, such as file decompression and read/write operations.

  1. Delayed initialization

That is, utility classes that are not currently applicable are deferred until they are ready to be used. Like reading colors from XML, consider reading and parsing them at use time.

  1. Thread optimization

The creation of threads consumes more system resources and reduces the creation of threads. Consider sharing a thread pool.

How to detect the creation of a thread

Stability optimization

Dimension of APP stability

Generally speaking, stable app refers to the normal operation of app. The normal operation of APP can be divided into two categories, namely Crash and ANR

Crash: An error occurs during a running process.

ANR: An operation performed by the system when the application is running again because the response cannot be completed within the specified period.

How to runCrash

An application Crash occurs when an application generates an unhandled exception (that is, an exception that is not caught by a try catch) while the application is running. This will cause the app to not work properly.

If you need to resolve, you need to know where the unhandled exception was generated, typically by analyzing the method call stack for the unhandled exception.

Android APP can be divided into two layers, Java layer and Native layer. So how to capture needs to be said separately.

The Java layer gets the call stack of unhandled exceptions

This requires understanding how the Java virtual machine reports an uncaught exception.

Uncaught exceptions are thrown up the method’s call chain until ThreadGroup’s uncaughtException method

    public void uncaughtException(Thread t, Throwable e) {
        if(parent ! =null) {
            // Recursive call, can be ignored
            parent.uncaughtException(t, e); 
        } else {
            / / to the Thread. GetDefaultUncaughtExceptionHandler () to handle an uncaught exception
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            if(ueh ! =null) {
                ueh.uncaughtException(t, e);
            } else if(! (einstanceof ThreadDeath)) {
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" "); e.printStackTrace(System.err); }}}Copy the code

Access code found that found ThreadGroup will eventually defaultUncaughtExceptionHandler Thread to process.

private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
Copy the code

The above code shows that: the Thread defaultUncaughtExceptionHandler is a static variable Thread class.

See here, how to capture the Java layer of an unhandled exception is very clear, to set up a new Thread defaultUncaughtExceptionHandler, In this new defaultUncaughtExceptionHandler collecting needed information.

Need to note is that the old defaultUncaughtExceptionHandler need to be preserved, and then new defaultUncaughtExceptionHandler after collecting information, Need to be transferred to the old defaultUncaughtExceptionHandler continue processing.

The Native layer retrieves information about unhandled exceptions

Having said that, let’s take a look at how the Native layer handles an unhandled exception. Native layer processing, need to master some knowledge of Linux, because I do not particularly understand Linux, here directly refer to others’ articles. If there are any mistakes, please correct them.

Through reference, I found that if unprocessed anomalies occur in Native layer (note: if the Native layer captures the anomalies, it can be thrown to the Java layer for processing through JNI), the system will send signals to Native layer. If unprocessed anomaly information is to be collected in Native layer, You need to register the corresponding signal handler. When an exception occurs, the Native layer receives the information and collects it through the processor.

The registration signal processing functions are as follows:

#include <signal.h> 
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
Copy the code
  • Signum: Indicates the signal code. It can be any specific valid signal except SIGKILL and SIGSTOP. If you define your own processing function for these two signals, the signal installation error will occur.
  • Act: pointer to an instance of the sigAction structure that specifies the processing of a particular signal. If set to null, the process will perform the default processing.
  • Oldact: similar to the act argument, but saves the original processing of the corresponding signal, or can be set to NULL.

With the signal processing function, the next thing to do is to collect information. Since I am not very familiar with the development of Native, I will not expand on it here. You can refer to the crash capture mechanism and implementation of Native code on the Android platform.

How to runANR

ANR is short for Applicatipon No Response. If the application freezes or responds too slowly, the system will kill the application. Why kill an app? In fact, it’s easy to understand, if you don’t kill the application, people will think the system is broken.

So how do we monitor ANR? And how do we analyze the ANR problem? What are the common causes of ANR?

First, the principle of ANR is that WHEN THE UI operation starts, AMS will start a delayed task according to the type of UI operation. If this task is triggered, it indicates that the application is stuck or the response is too slow. This task is removed at the end of the UI operation.

Then, how do you analyze the ANR problem?

Generally, when ANR occurs, information related to ANR will be printed in logcat, which can be seen by filtering keyword ANR. Detailed analysis is not required here, but you can refer to the following article.

A tetrace.txt file is generated in the /data/anr directory, which contains information about the system at the time the ANR occurred, and the threads used for all applications (note that different ROMs may vary). Most of the ANR problems can be solved by using logcat and tracted. TXT. However, some of the ANR problems cannot be solved because logcat and tracted. TXT provide limited information. In some cases, there is no particularly useful information, especially Android permissions have been tightened, and the testamp.txt file cannot be read in higher Android versions, which adds a lot of difficulties to ANR analysis. However, fortunately, I recently found that the headlines wrote a series of articles for ANR, in which I think the governance methods for ANR problems are very good. I would like to quote here.

  • Toutiao ANR Optimization Practice series – Design principles and influencing factors
  • Toutiao ANR Optimization Practice series – Monitoring tools and analysis ideas
  • Toutiao ANR optimization practice series sharing – case analysis collection
  • ANR Barrier causes the main thread to fake death

I have written a small performance monitoring tool, which has the function of monitoring UI thread blocks, consider adding ANR monitoring mechanism of headlines later, when the follow-up is completed, I will make a detailed summary. So much for this summary.

Memory optimization

The memory of the hardware is always limited, so is the memory allocated to each application. It is necessary to optimize all the memory, otherwise the application will not have enough memory to use, and then the Crash will occur.

Where is the memory going

If you optimize memory, you need to understand where the memory is consumed, and optimize the scene with large memory consumption. Only by taking the appropriate medicine can you have a good optimization effect.

The Profiler tool in Android Studio is a great tool to use to monitor the memory allocation of your APP in real time.

After the APP memory stack is dumped, you can also see how much memory each class occupies.

You can view detailed information about each object.

Android Studio Profiler is a Profiler tool that you can use in Android Studio.

How to use memory wisely

Using the above method, to find a large memory consumption of the scene, you need to do optimization, the main approach is to find a way to reduce the memory use in a particular scene. Personal summary of the usual may do optimization.

  • Image related optimization

The picture is a relatively large piece of memory in the application I do now, and I have encountered some problems. I mainly optimize it through the following methods.

  1. Do not load images that are not needed at the moment. For example, there is a network loading exception. Do not initialize the image at the beginning, wait until there is an exception that needs to be displayed
  2. When loading images, try to load images of the specified size, because sometimes the control size is smaller than the actual image size, this time, some memory will be wasted. If necessary, you can have the background return images of different sizes.
  3. Depending on the image format
  4. Do not display pictures, you can consider releasing first.
  • Create as few objects as possible

There is no doubt that if there are fewer objects, memory consumption is certainly less, so what should we pay attention to at ordinary times?

  1. When customizing views, don’t create objects too often in the onDraw method. Because the onDraw method may be called frequently, a large number of objects will be created. This causes waste, but also increases the frequency of GC triggers, resulting in stuttering.
  2. Create as few threads as possible. Creating a thread is a resource consuming process. Creating an empty thread will take up 1-2 M of memory. Also, asynchronous tasks are usually executed very quickly. If you create threads frequently to do asynchronous tasks, in addition to high memory usage, GC may also cause lag. For asynchronous tasks, it is generally recommended to use thread pools, but pay attention to the use of thread pools.
  3. Try to concatenate strings with StringBuilder or StringBuffer. The most common problem is that a large number of strings are created when logCAT is printed and when the data returned in the background is concatenated, so if there is a similar situation, you can also consider some optimization.

What is a memory leak

A memory leak is when the memory that should have been freed is held by the GC ROOT object for some reason and cannot be freed during GC. One of the problems that can result from this is that the APP does not have enough memory to use after repeated operations, and the system will kill the APP. So memory leaks need to be checked.

How do I monitor and analyze memory leaks

The previous summary concluded that the above is a memory leak because some GC ROOT objects hold objects that are expected to be freed, causing the expected memory to not be freed in time. So how to monitor and analyze memory leaks becomes a question of how to find the GC ROOT.

The usual steps for manual analysis are to repeat the suspected memory leak scenario and then trigger several GC’s. After a few seconds, dump the APP stack (you can use the AS tool dump), then use the SDK’s Cover tool to convert, and then use the MAT tool to analyze the memory leak object to GC ROOT’s reference chain.

Manual analysis is always a hassle, but the good news is that there is a particularly useful tool for automatically monitoring and analyzing memory leaks, and this tool is LeakCanary, which automatically monitors and gives the leaky objects a reference chain to GC ROOT.

It’s easy to use, just add it under build.gradle in your APP

dependencies { // debugImplementation because LeakCanary should only run in debug builds. debugImplementation 'com. Squareup. Leakcanary: leakcanary - android: 2.7'}Copy the code

One of the core principles behind leakCanary is to make use of a feature of weak references, which is:

When creating a weak reference, you can specify a RefrenceQueue. When the reachability of the weak reference object changes, the system will place the weak reference object in the previously specified RefrenceQueue for processing.

After GC, leakCanary will dump the heap and use the shark library to analyze the heap. Find a shortest reference chain to GC ROOT and prompt.

Common memory leak scenarios

I summed up some scenes of memory leakage in my work, and recorded them for your reference.

  1. Static variables hold Context and so on.
  2. Singleton instances hold Context and so on.
  3. Some callbacks do not de-register, such as broadcast registrations and de-registrations, and sometimes some third-party libraries need to be taken care of.
  4. Some Listeners do not disconnect manually.
  5. Anonymous inner classes hold instances of external classes. Common implementations of anonymous inner classes such as Handler, Runnable, and so on often accidentally hold instances of external classes such as Context.

Operation fluency optimization

Why is it stuck

Before we get stuck, we need to learn a little bit about hardware. Is in the process of interface drawing, the main task of THE CPU is to calculate all View on the screen corresponding graphics and vector information. The main task of GPU is to raster the graphics calculated by CPU and convert them into bitmaps, which can be simply understood as the corresponding values of pixels on the screen.

If the operation is stuck, one or more of the CPU and GPU cannot complete the corresponding task in a short time.

Generally speaking, CPU in addition to the need to calculate the View corresponding graphics and vector information, but also do logical operations and file read and write tasks, so CPU caused by the more common. It is also generally optimized by reducing CPU computation tasks.

The CPU usage is affected in the following ways:

  • Read and write files
  • Parsing lots of pictures
  • Frequent Request network
  • Complex layout
  • Creating objects frequently

How to detect caton

Although we know roughly what causes the lag, we cannot accurately locate the code point of the problem. In view of the above part of the problem, I have written an open source library to automatically detect it. The address of the open source library is

Github.com/XanderWang/…

Detailed principle, can refer to the above connection, here is a simple summary of the principle of monitoring UI card section.

As we know, in Android, interface refreshes are performed by the main thread or UI thread. The interface starts with a Looper message loop. Looper Message loop mechanism has an interesting feature, that is, Looper will use Printer to print specific tag strings before and after dispatch when it dispatches Message. We can get the timing before and after the Dispatch message.

We can then start a delayed task fetching system information on the asynchronous thread before the Dispatch message. After the dispatch message, we can remove this delayed task from the asynchronous thread. If the execution of a message does not exceed the threshold, the delayed task in the asynchronous thread is cancelled, indicating that there is no lag. If the execution time of a message exceeds the threshold, the delayed task in the asynchronous thread will execute, indicating that there is a delay. The delayed task in the asynchronous thread will obtain the system status at this time, so as to assist us in analyzing the delay problem.

How to optimize caton

So with that out of the way, let’s talk about optimization. In why will caton summary I summarized several common, now several scenarios of optimization summary.

Read and write files

One of the most common ways to read and write files without realizing it is the use of SharePerfrences. When using SP, you need to be careful not to call apply or commit frequently, because every call may lead to a write operation. Not necessarily able to write documents). Therefore, if you call the file many times, you will write the file many times, which is a time-consuming and resource-consuming operation, so do it less.

The general optimization method is to split sp files reasonably, one SP file should not contain too many items, and the content of each item should be as short as possible. Try to commit data in batches before committing or applying. Note also that commit directly triggers writing to the file (when the content changes), so calling commit from the UI thread may block the UI thread.

If you have higher performance requirements, consider replacing sp with MMKV or DataStore. I won’t go into the details of the substitution. There are many references on the Internet.

Another common scenario for reading and writing files is to read layouts, color values, and so on from XML files, which are IO operations. To read layouts from XML, consider creating views directly in code. To read colors from XML, consider adding a HashMap.

Parsing lots of pictures

Decoding images is undoubtedly a computatively expensive operation, so it is best to compress images according to the actual display size and save the compressed thumbnails for direct loading next time.

In addition, it should be noted that the loading of images is controlled during the scrolling process of the list. Generally, images are not loaded during the sliding process of the list, and images are loaded only after the scrolling of the list stops.

Another way to optimize is to reduce the use of images, but this is a bit more difficult.

You can also consider using different decoding formats for different picture formats. For example, PNG images can be decoded in 8888 or 4444 decoding mode according to the actual situation of the machine. For JPG/JPEG images, use 565 decode mode to decode the image. I have not tested whether the efficiency of decoding images with different decoding methods will be high, but there is no doubt that memory usage is different.

Frequent Request network

Network request, can refer to the following optimization method.

  1. If you are using okHTTP to request the network, try to use an HttpClient globally. The advantage of this is that it can be reused and improve the efficiency of network requests.

  2. If the background is supported, enable GZIP compression. In this way, the amount of data transmitted over the network is smaller and the transmission efficiency is higher.

  3. Customize DNS to reduce DNS resolution time.

  4. Through consultation with the background, part of the data background interface in one step, as far as possible to avoid multiple requests to get the complete target data.

Complex layout

If the layout is complex, the CPU does a lot of computation to determine the final shape. So if the layout is complex, the CPU needs a lot of computing resources, so it is necessary to optimize the complex layout.

  1. Reduce layout levels. You can use tags such as ViewStub, Merge, and include to try to reduce layout levels.

  2. Using efficient layout containers, such as ConstraintLayout, allows you to achieve complex effects without nesting layout containers.

  3. Part of the effect can be considered with a custom View implementation.

This optimization feels not particularly easy to do, it may be optimized, but the effect is not good, but it has to do.

Creating objects frequently

Why is this listed? Because objects are created frequently, they can consume a lot of memory for a short period of time, and when they run out of memory, the system tries to reclaim objects by GC, which is a very resource-intensive operation. Although The Android system has made a lot of improvements to GC, it is always good to minimize GC triggering.

Common scenarios in which objects are frequently created are:

  • When customizing a View, create temporary objects in the onDraw method
  • Loops use “+” to concatenate strings
  • The initial capacity of container classes with limited capacity, such as ArrayList, is inappropriate, resulting in frequent expansion of newly added data.

There may be some scenarios that haven’t been listed yet, so if you have any good suggestions, feel free to make them.

In addition to frequent object creation, GC can also be triggered if too much memory is used, such as showing a large Bitmap, which can be shown as a thumbnail, but may involve zooming in to see the details. This time can consider the use of cutting display region (BitmapRegionDecoder) to parse the picture.

summary

The above are the optimization points involved in my work, which may not be very complete and inevitably there are omissions. If there are any mistakes or omissions, please correct them.

To contact me

  • Github: github.com/XanderWang

  • Mail: [email protected]

  • Blog: xander_wang. Gitee. IO/android – not…

  • Juejin: juejin. Cn/user / 211951…