In this paper, starting from vivo Internet technology WeChat public links: mp.weixin.qq.com/s/aRDzmMlkq… Author: Xu Jie

Different Android versions have different memory processing methods for an image. Incorrect use will lead to OOM. This article will help you sort out the memory usage and select the image loading mode suitable for you to solve OOM problems.

The background,

You know what?

  1. How much memory does a 5.48MB, 24bit static image take up under the res/drawable-[density]/ different folder under the Android project directory?

  2. How much memory does it take to load a 5.48MB, 24bit network picture with 4896*6528 pixels?

! [](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy80ZzVJTUdpYlN4dDVVM3BzZE5pY0Y0SHI1aGljU1JJZTh RR1hOZ0lqaWJQQzFZSEQ0TG9mQlNONTJ2OGVLUGE3azdqUWtYMWJrT0dsMVFWVm9TT3FMUGVUUncvNjQw?x-oss-process=image/format,png)

(Image: 4896*6528 pixels)

Second, sort out the concept

Before we dive into what follows, let’s look at a few concepts.

1. Screen size

The diagonal length of a screen, in inches (1 inch =2.54 cm). This value takes the length and width of the phone screen, and then using the Pythagorean theorem, you can figure out the length of the hypotenuse.

2. Screen pixel density

Ppi refers to the number of pixels per inch of a screen. Screen pixel density is related to screen size and screen resolution. The lower the screen density, the fewer pixels in a given physical area. Android divides all screen densities into six universal densities: LDPI (low), MDPI (middle), HDPI (high), XHDPI (super high), XXHDPI (super super high), and XXXHDPI (super super high).

3. Screen resolution

Screen resolution refers to the number of pixels in horizontal and vertical, the unit is px, 1px=1 pixel point, for example, we often say that the width and height of pixels: 4896*6528.

Are the above three concepts vague? We can take a look at these two images to clarify the above three concepts:

! [](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy80ZzVJTUdpYlN4dDVVM3BzZE5pY0Y0SHI1aGljU1JJZTh RR1VKUkVtWmNDRFJUekY1WUNiU1FYeTh6TmJzUVV1aWM4TERJTUVuSThnbU9taWNJS21xQ3p0NnV3LzY0MA?x-oss-process=image/format,png)

(Figure: Resolution calculation formula)

For the following analysis, it is important to know the screen pixel density.

Three, screen density (DPI) correspondence

The number of pixels in the physical area of the screen, often called DPI (points per inch). The less dense the screen, the fewer pixels there will be in a given physical area. Android divides all screen densities into six universal densities: LDPI (low), MDPI (middle), HDPI (high), XHDPI (super high), XXHDPI (super super high), and XXXHDPI (super super high).

A scaling ratio of 3:4:6:8:12:16 is followed between the six universal densities.

Fourth, code verification

The code is simple: an ImageView contains a background image, and then converts it to a Bitmap to see how much memory it uses.

The layout file activity_main.xml

The layout file, which is an ImageView control, contains a background image.

MainAcivity.java

Android has a special folder res/drawable-nodpi/. The resources stored in this folder will not be enlarged or compressed, and will be displayed as the original size. We also put the test resources in this folder.

5. Memory usage of pictures

1, static images do not distinguish folder memory usage

For example, if the width and height of the image is 4896*6528=31961088, the original size of the image is 5.48m, and the image resource is placed under res/drawable-nodpi/, then find a Vivo X21 phone and load the image, the memory size is 127844352byte:

The total number of pixels in the original image of the image is 31961088, which seems to have nothing to do with the memory size of 127844352byte, but the truth is that 31961088* 4 = 127844352(Byte), and the size of the original image is multiple of the size of the final memory occupation. In this case, the direct relationship between memory usage and image size is the original image size (e.g. 480×800). I understand that, but where does the multiple relationship come from? This is to talk about the pixel format of Bitmap.

Android supports 4 pixel formats, source code in bitmap.config:

In order to ensure the image quality, the official default format is ARGB_8888, which causes each pixel of the image to occupy 4 bytes. Therefore, the memory size of the image in demo is the total number of pixels * pixel format, which is 384000 * 4 = 1536000(bytes). Can help you solve some of the actual project problems.

2, static pictures distinguish folder memory occupation phenomenon

(1) Static images distinguish the memory usage of folders on X21 (Android 8.0)

So again, it’s ok to put it in res/drawable-nodpi/, what about other folders? Because we have to adapt to different machines.

Again using vivo X21 as an example, the target image folder is RES /drawable-xxdpi/ and the screen density is 480dpi.

Take a look at the memory usage of this picture under different folders, in unit: M.

As you can see,

  1. For res/drawable-hdpi/, RES /drawable-xhdpi/, res/drawable-xxdpi/, images occupy basically the same memory, Java layer memory is not consumed, but consumes native memory.

  2. Res /drawable-xxxdpi/ resolution below, the memory is the highest, native occupied 200M.

(2) Is this the rule for all machines

Maybe you have this question:

Why do images occupy the same amount of memory under different folders, but sometimes they occupy different amount of memory under different folders?

Before answering this question, you should be aware that Google makes a distinction between the Native stack and the Java stack for different Android versions when loading images.

There is also an interesting phenomenon here, on Android4.4 to Android 8.0 or below, when you put the image in a different folder, the image will take up different memory, that is because the image memory is loaded in the Java stack, so you may encounter the Java layer OOM.

AndroidRuntime: java.lang.RuntimeException: Canvas: trying to draw too large(127844352bytes) bitmap.
Copy the code

In fact, pixel memory is allocated in the Native layer by calling Calloc directly, so its pixels are allocated on the Native heap. This is why bitmap memory consumption after 8.0 can grow indefinitely. The reason for the Java OOM is not prompted until the system runs out of memory.

3. Network image loading occupies memory

(1) Glide loading picture method

Glide loads image resources in two ways:

  • No callback, use the following method to load
Glide.with(context)
        .load(url)
        .apply(requestOptions.override(width, height))
        .into(imageView);
Copy the code

There is a callback, use the following loading method, the difference is passed into simpleTarget instead of ImageView

Glide.with(context)
        .asBitmap()
        .load(url)
        .apply(requestOptions)
        .into(simpleTarget);
Copy the code

SimpleTarget is defined in two ways:

  • The width and height parameters are passed and are greater than 0
simpleTarget = new SimpleTarget<Bitmap>(width, height) {}
Copy the code
  • Width and height are 0
simpleTarget = new SimpleTarget<Bitmap>() {}
Copy the code

(2) Problems caused by SimpleTarget usage errors

  • The difference between A and B

The difference is that when you pass in the width and height, the image is cached in memory according to the size you pass in. When you do not set the width and height, the image is cached in its original pixel size.

  • But we often don’t pass in width or height

This is because when loading a web image, we often don’t know what the width and height are. When we set the local resource imageView pixel, we use wrap_content or match_content, and we don’t know the final width and height, so we pass in width = 0, height = 0, Use Glide to download the picture, and then do the corresponding Settings.

  • Why don’t we generally feel the difference between A and B

This is because no matter the network image or the local image, the pixel is not too large. Take the pixel type RGB_8888 as an example, a picture of 19201080 occupies the memory of 19201080*4Byte = 829440Byte = 7.9m.

At this time set the width and height (normal also set a few dozen DP) and do not set the width and height, the difference is not big.

  • Collapse to the

04-27 17:39:53. 154, 31269-31269 /? E/art: Throwing OutOfMemoryError "Failed to allocate a 227278860 byte allocation with 1048576 free bytes and 126MB until OOM"Copy the code

  • Why did it crash?

Although a local image is 5.48m in size and width = 4896 in pixel height = 6528, it occupies 4896 * 6528 * 4 = 127844352byte = 120M in memory. This amount of memory is enough to cause the official website app to flash back when it was already using too much memory.

Take a look at the memory load of this local image, from 320M to 548M, up 228M (and background events that cause memory fluctuations, the root cause of the flash back is the Graphics memory surge).

  • How to fix the crash?

Try to get rid of simpleTarget’s B definition method

If you don’t know what the realistic width and height of the resource should be, set the following parameters so that the current screen width and height are the highest display pixels and downSAMPLE is set to downSampleStrategy.at_most.

This means:

When the original size of your resource is greater than width * height, use width * height.

When the original size of your resource is less than width * height, use the original size.

Width * height is the maximum pixel value when the image is saved to memory.

The flash rollback problem is also resolved. In this case, the memory usage increases by 50M from 290 MB to 340 MB (and memory fluctuations caused by background events).

Six, summarized

  1. Static resource images of different resolutions are placed in different folders. Do not put them randomly, which will cause memory abnormalities.

  2. Network loading framework Glide and so on, it is best to set the width and height of the picture that needs to be loaded according to the screen width and height, do not use the original size of the picture to load, otherwise it is easy to crash.

Others: If you are interested, you can verify the memory usage of Android 8.0 below, you will find a different world.

For more content, please pay attention to vivo Internet technology wechat public account

Note: To reprint the article, please contact our wechat account: Labs2020