Detailed explanation of Android memory optimization strategy

  • 2021-12-19 06:43:29
  • OfStack

Preface 1. Memory optimization strategy 2. Specific optimization points 1. Avoid memory leakage 2. Optimization strategy for large objects such as Bitmap (1) Optimize Bitmap resolution
(2) Optimize single pixel memory
(3) Caching strategy of Bitmap
(4) Select the appropriate drawable folder for drawable resources
(5) Optimization of other large objects
(6) Avoid memory jitter
3. Native API callback releases memory 4. Memory troubleshooting tool (1) LeakCanary monitors memory leaks
(2) Monitoring memory through Proflier
(3) Troubleshooting memory leaks with the MAT tool
Summarize

Preface

Before you start, you need to understand one question. Why do you want to do memory optimization? Or what is the purpose of memory optimization?

1. Memory optimization strategy

Memory optimization 1 from two directions to start optimization, 1 is the first blog to prevent memory leaks, to avoid unnecessary waste of memory resources; Another aspect is the optimization of large objects in APP, which reduces the memory occupied by large objects.

2. Specific optimization points

1. Avoid memory leaks

Just read the last blog directly here:
Detailed explanation of Android memory leakage and optimization scheme

2. Optimization strategy of large objects such as Bitmap

Image loading is the main culprit of memory usage, and it is also the most common, so optimizing the memory usage of bitmap is very critical.
The memory calculation formula of Bitmap is as follows:

Bitmap memory occupied = resolution * memory of a single pixel

For example, a 1920 * 1080 image takes up the memory of a single pixel of 1920 * 1080. Therefore, the optimization of Bitmap can be carried out from two aspects: resolution and single pixel.

(1) Optimize the resolution of Bitmap

Usually, when APP loads a picture, the size of ImageView is determined. For example, the size of an ImageView is set to 100 * 100, but the resolution of the loaded Bitmap is 200 * 200, so the resolution of the 'Bitmap' can be compressed to 100 * 100 'by sampling compression. By this compression operation, the memory size can be directly reduced by 4 times. The code is as follows:


val options = BitmapFactory.Options()
options.inSampleSize = 2 //  Set the sampling rate to 2 Will be picked every two pixels 1 Pixels, and the final resolution width and height become the original  1/2
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.image, options)

(2) Optimize single pixel memory

Image 1 in computer is generally composed of three channels of red, green and blue plus one transparent channel. Therefore, a pixel is also composed of red, green, blue and a transparent channel, and the corresponding memory is represented by byte. For example, if two byte are used to store one pixel, each channel will occupy 4 bit memory, while if four byte are used to store one pixel, each channel will occupy 1 byte. 4-byte pixels can represent more colors than 2-byte pixels, so the image quality composed of 4-byte pixels is clearer. (1 Byte is composed of 8 bits, which is the basic unit of data storage. 1Byte is also called 1 byte.)

In the Bitmap of Android, the memory occupied by a single pixel is related to the inPreferredConfig parameter configuration of Bitmap. The code is set as follows:


     final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;// Parse only the edges of the picture to get the width and height 
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        BitmapFactory.decodeFile(filePath, options);
        //  Calculate the scaling ratio 
        options.inSampleSize = calculateInSampleSize(options, desWidth, desHeight);
        //  Complete parsed picture returns bitmap
        options.inJustDecodeBounds = false;
        Bitmap bm = BitmapFactory.decodeFile(filePath, options);

options. inPreferredConfig = Bitmap. Config. RGB_565; The parameters set are as follows:

Config设置 占用内存(byte) 备注
ALPH_8 1 只包含1个透明通道,透明通道占用 8bit,即 1byte
RGB_565 2 包含R/G/B3个颜色通道,不包含透明通道,3个通道占用的内存分别为5bit/6bit/5bit
ARGB_4444 2 已废弃,包含A/R/G/B4个颜色通道,每个通道占用4bit
ARGB_8888 4 24位真彩色,Android默认配置,每个通道占用 8bit
RGBA_F16 8 Android 8.0 新增,每个通道占用16bit,即两个字节

In the Android system, the default color mode of Bitmap is ARGB_8888, that is, each pixel occupies 4byte, so by default, a picture with resolution of 1920 * 1080 occupies a memory size of 1920 * 1080 * 4 = 7.91 M after being loaded into memory

The corresponding color mode can be set by setting inPreferredConfig parameters. For example, for a picture without transparent channel, we can set it to RGB_565, which ensures the quality of the picture and reduces the memory occupation.
At this point, a 1920 * 1080 image loaded into memory has a memory size of 1920 * 1080 * 2 = 3.955 M, which is 1.5 times less than the default memory footprint.

(3) The cache strategy of Bitmap

The memory occupation problem can also be optimized to a certain extent through cache strategy. For example, the Glide framework adopts a three-level local cache strategy to optimize Bitmap, and sets active cache, LRU memory cache and local cache. For ImageView with the same parameters, only one copy is saved in memory to reduce the memory size.

(4) Select the appropriate drawable folder for drawable resources

For example, we only put a picture of 100 * 100 in the directory of hdpi, so according to the conversion relationship, the mobile phone with resolution matching xxhdpi will be stretched to 200*200 when referring to this picture. It should be noted that in this case, the memory footprint will increase significantly. For images that do not want to be stretched, you need to put them in the directory of assets or nodpi.

(5) Optimization of other large objects

Lightweight data structures can be used. For example, we might consider using ArrayMap/SparseArray instead of traditional data structures such as HashMap, which in most cases shows inefficiency and takes up more memory than the ArrayMap container specially written for mobile operating systems in Android systems. In addition, SparseArray is more efficient because it avoids automatic packing of key and value, and avoids unpacking after packing.

(6) Avoid memory jitter

Memory jitter refers to the sudden creation of a large number of objects in a short time, which frequently causes GC recycling, resulting in memory fluctuations. Frequent creation of objects should be avoided in development to avoid memory jitter. Because memory jitter will trigger GC frequently, and GC will cause STW problem, which will directly affect the performance of the program.

For example, when drawing a custom View, 1 avoid creating objects in onDraw or onMeasure.

3. Native API callback frees memory

The Android system provides a number of callbacks to inform the current application of memory usage, such as the following two methods:

onLowMemory () Generally, when all Background applications are dropped by kill, forground applications receive a callback from onLowMemory (). In this case, it is necessary to release the unnecessary memory resources of the current application as soon as possible, so as to ensure that the system can continue to run stably. In particular, to free the Bitmap resource cached in Glide, recycle the resource by calling the Glide. onLowMemory method.

onTrimMemory () The Android system has also provided a callback to onTrimMemory () since 4.0, When the system memory reaches certain conditions, All running applications will receive this callback, At the same time, the following parameters are passed in this callback. On behalf of different memory usage, when receiving onTrimMemory () callback, it is necessary to judge according to the type of parameters passed, and reasonably choose to release its own memory occupation. On the one hand, it can improve the overall running smoothness of the system, and on the other hand, it can avoid being judged by the system as an application that needs to be killed first. For example, call Glide. onTrimMemory () to recycle bitmap.

4. Memory troubleshooting tools

(1) LeakCanary monitors memory leaks

In debug mode, LeakCanary will be turned on to detect the memory leak problem, and the reference link provided by LeanCannary can quickly locate the memory leak location.

(2) Monitoring memory through Proflier

After one function is developed, the memory usage of APP can be detected by Profiler. Repeatedly open and close the page, and then trigger GC, whether the memory can be reduced.

(3) Troubleshooting memory leaks with the MAT tool

MAT provides a very powerful function, can view the object deep heap, shallow heap memory size and so on.

Summarize

Usually development for this concern is not a lot, may not be considered before the lack of memory problems, the requirements of the project is not so high, after learning these points need to slowly pay attention to these issues in development.


Related articles: