This is probably the best performance tuning tutorial series column this is probably the best performance tuning tutorial series this is the best performance tuning tutorial series

preface

Memory leaks have always been a topic of concern, whether it’s Android Studio’s built-in memory leak analysis tool, Eclipse MAT, or the much-loved third-party plugin LeakCanary. Memory optimization is essential if you are addressing memory leaks at their root. Therefore, in this chapter, we refer to the memory optimization strategy of Hu Kai of throwline, and directly take out a chapter to talk about memory optimization.

Memory optimization can be basically divided into the following aspects

  • Reduce the memory footprint of objects
  • Reuse memory objects
  • Avoid memory leaks of objects
  • Optimized memory usage policies

Reduce the memory footprint of objects

Avoid using enUms in Android

Enum is a Java data type that contains a fixed constant. We can use Enum whenever we need to know a number of predefined values that represent data classes. We usually use enums to do some compile-time checking to avoid passing in invalid arguments.

However, each Object of an Enum is an Object, and it has been explicitly stated on the Android official website that Enum should be avoided in Android development because it requires much more memory than static constants.

Therefore, in practice, I prefer interface variables because the interface automatically sets member variables to static and final, which prevents the error of adding new constants in some cases and makes the code look simpler and clearer.

Use more lightweight data structures

As mentioned in the first section, we should think more about using ArrayMap and SparseArray than traditional data results like HashMap, which has been illustrated briefly, Compared with Android’s ArrayMap container, which was written specifically for mobile operating systems, the display is inefficient and more memory intensive in most cases. The usual implementation of HashMap is more memory intensive because it requires an additional instance object to record the Mapping operation. Additionally, SparseArray is more efficient because they avoid autoboxes for keys and values, and avoid unboxing after boxing.

Use smaller images

When designing the resource image, we need to pay special attention to whether there is space to compress the image and whether a smaller image can be used. Trying to use smaller images not only reduces memory usage, but also avoids a lot of inflationExceptions. If a large image is referenced directly by an XML file, it is likely that an InflationException will occur when initializing the view due to insufficient memory. The root cause of this problem is that an OOM error occurred.

Reduce the memory footprint of Bitmap objects

Bitmaps consume a lot of memory. It is important to reduce the memory footprint of bitmaps created. Generally speaking, there are two measures:

  • Before loading images into memory, we need to calculate an appropriate scale to avoid unnecessary loading of large images.
  • Decode format: Decode format. Select ARGB_8888 / RBG_565 / ARGB_4444 / ALPHA_8, which varies greatly.

Use ints whenever possible

In Android, the data access speed of float is half that of int. Int is preferred. Instead of an Integer, which is also a synonym for an Integer, using an int instead of an Integer will reduce your memory overhead.

Reuse memory objects

Reuse system resources

Android has a lot of built-in resources, such as strings/colors/images/animations/styles and simple layouts, that can be referenced directly in an application. This can not only reduce the load of the application itself, reduce the size of APK, but also reduce the memory overhead to a certain extent, better reuse. But it is also necessary to pay attention to the Android system version differences, for those different system versions of the performance is very different, do not meet the needs of the situation, or the application itself built-in.

Note that the ListView/GridView Adapter multiplexes the ConvertView

This seems like nothing to talk about, it’s too basic, and we probably prefer RecyclerView now.

Use StringBuilder whenever possible

This is also very basic, so let’s stop there. It’s basically using StringBuilder/StringBuffer as much as possible to replace our frequent string concatenation.

Try to use subString from the original string

When extracting a String from an existing dataset, try to return a subString of the original data rather than creating a duplicate object.

Avoid creating objects in onDraw()

Frequently called methods such as onDraw() must be avoided because they rapidly increase memory usage and can easily cause frequent GC and even memory jitter.

Avoid memory leaks of objects

The leakage of memory objects will lead to some objects that are no longer used cannot be released in time. In this way, precious memory space will be occupied on the one hand, and OOM will appear when the memory needs to be allocated later due to insufficient free space. Obviously, this also makes the memory area available space for each level of Generation smaller, gc will be more easily triggered, prone to memory jitter, resulting in performance problems.

Notice Activity leaks

Generally speaking, Activity leakage is the most serious problem in memory leakage. It occupies a large amount of memory and affects a wide range of areas. We need to pay special attention to the following two cases of Activity leakage:

  • The most typical scenario for an Activity to leak due to an internal class reference is when an Activity leaks due to a Handler. If there are delayed tasks in the Handler or the queue of tasks waiting to be executed is too long, It is possible for the Activity to leak because the Handler continues to execute. The reference chain is Looper -> MessageQueue -> Message -> Handler -> Activity. To solve this problem, you can execute the Messages and Runnable objects in the Remove Handler message queue before the UI exits. Or Static + WeakReference can be used to disconnect the reference relationship between Handler and Activity.

  • The Activity Context is passed to other instances, which can cause itself to be referenced and leak. Leaks caused by inner classes don’t just happen on activities. You need to watch out for them anywhere else! We can consider using static internal classes as far as possible and using WeakReference mechanism to avoid leakage due to cross-reference.

Use the Application Context whenever possible

For most cases where you don’t have to use an Activity Context (the Dialog Context must be an Activity Context), We can all consider using the Application Context instead of the Activity Context to avoid inadvertent Activity leaks.

And if it is an Activity Context that Glide needs to pass, it will crash if it is not loaded after the Activity is destroyed. So please pass the Application Context directly when using Glide, Toast, etc.

Note that the Cursor object is closed in time

In the program, we often conduct database query operations, but there are often careless use of Cursor did not close in time. The leakage of these cursors will have a great negative impact on memory management. We need to remember to close the Cursor object in time.

Note the WebView leak

Android WebView has a great compatibility problem, not only the Android system version of the WebView has a great difference, in addition to different manufacturers shipped ROM WebView also has a great difference. Even worse, the standard WebView has a memory leak problem, see here. Therefore, the solution to this problem is to start another process for WebView, communicate with the main process through AIDL, and the process where WebView is located can choose the right time to destroy according to the needs of business, so as to achieve the complete release of memory.

Note the timely collection of temporary Bitmap objects

Although in most cases we add caching mechanisms to bitmaps, at some point some bitmaps need to be recycled in a timely manner. For example, when a relatively large Bitmap object is temporarily created, the original Bitmap should be reclaimed as soon as possible after the transformation to obtain a new Bitmap object, so that the space occupied by the original Bitmap can be released more quickly.

Of particular note is the createBitmap() method provided in the Bitmap class:


Note the listener logout

There are many registers and unregisters in Android applications, and we need to make sure that we unregister those listeners at the right time. Add the listener manually. Remove the listener in time.

Optimized memory usage policies

Use large heap sparingly

Android devices have different sizes of memory depending on hardware and software Settings, and they set different Heap limit thresholds for applications. You can get the Heap size available for your application by calling getMemoryClass(). In some special cases, you can declare a larger heap space for your application by adding a largeHeap = true attribute to the application tag in the manifest. You can then get the larger heap size threshold by using getLargeMemoryClass(). However, the intention of declaring a larger Heap threshold is for a small number of applications that consume a lot of RAM (such as a large image editing application). Don’t ask for a large Heap Size just because you need to use more memory. Use large heap only when you know exactly where a lot of memory is being used and why it must be reserved. Therefore, use the large Heap attribute with caution. Using extra memory space affects the overall user experience of the system and can make each GC run longer. During task switching, the performance of the system is severely compromised. In addition, a large heap does not necessarily capture a larger heap. On some strictly limited machines, the size of the large heap is the same as the normal heap size. So even if you apply for a large heap, you should check the actual heap size obtained by executing getMemoryClass().

Select a proper folder for storing resource files

We know that pictures under folders of different DPI, such as HDPI/XHDPI/xxHDPI, will be processed by Scale on different devices. For example, if we only place a 100 x 100 image in the HDPI directory, the xxHDPI phone will be stretched to 200 x 200 based on the conversion relationship to reference that image. Note that in this case, the memory footprint is significantly higher. For images that do not want to be stretched, put them in assets or nodpi.

Try catch operations that allocate large amounts of memory

In some cases, we need to evaluate the code that is likely to get OOM. For those that are likely to get OOM, we can add a catch mechanism and consider trying a degraded memory allocation operation in a catch. For example, when decode bitmap, you can catch OOM, and try decode again after doubling the sampling ratio.

Use static objects with caution

Static objects should be used with caution on Android because they have a long life cycle and are consistent with the application process.

Pay special attention to unreasonable holdings in singletons

Although the singleton pattern is simple and practical and provides a lot of convenience, because the life cycle of the singleton is consistent with the application, it is easy to leak the holding object if it is used improperly. In particular, references that hold Context need to be treated with caution.

Optimize layout levels to reduce memory consumption

The flatter the view layout, the less memory it takes up and the more efficient it is. We need to make the layout as flat as possible, and consider using a custom View to achieve this when the system-provided View is not flat enough.

Use multiple processes with caution

Use more process can be part of the application components running in a separate process, so that we can expand the application range of the footprint, but this technology must be used carefully, the vast majority of applications use multiple processes should not be rushed, on the one hand, because of the use of multiple processes will make the code more complex logic, and if use undeserved, It may instead result in a significant increase in memory. Consider using this technique when your application needs to run a resident task in the background that is not lightweight.

A typical example is to create a Music Player that can play in the background for a long time. If the entire application is running in one process, there is no way to release the UI resources in the foreground when playing in the background. An application like this can be split into two processes: one for the UI and one for the Service in the background.

Write in the last

Memory optimization does not mean that the less memory your application consumes, the better. If gc operations are frequently triggered to keep the memory footprint low, the overall performance of your application will be reduced in some way, so there is a trade-off to be made.

If you want to receive the updated information in the first time, you can follow my Jane book: Jane book address, you can also choose to follow my public account: Nanchen