Another look at the Glide image loading library from a memory optimization perspective

Front background

As a commonly used image loading framework, Glide has a lot of memory optimizations at the framework level. However, as an image framework, ensuring correctness must be the first priority. Therefore, some additional optimizations can be made in appropriate scenarios at the application layer. , of course you need to understand the problems that may arise from optimization settings. In addition, the complexity of the framework design is very high, but the API exposed to the upper layer is very simple, which leads us to ignore some basic working principles and use the framework intentionally or unintentionally.

This is not an article about Glide source code analysis. If you are not familiar enough with using Glide, it is not recommended to read it directly. The following documentation conclusions are based on Glide v4+.

Some features of Glide

  1. The size of the bitmap loaded into memory by Glide without other additional configuration will not exceed the view size itself, so there will be no memory waste. For example, the original size of a network image is 200×200, but the size of the ImageView control displayed on the page is 100×100, and the final decoded image size within Glide is 100×100.
  2. If the size of the network image > the width or height of the control, it will be enlarged proportionally by default and a bitmap that satisfies the width-to-height ratio will be returned.
  3. Glide v4+ starts image loading returning ARGB_8888 format as the default DecodeFormat.
  4. If the RGB_565 format is used when loading the image, the actual operation may not be effective. This is mainly affected by two aspects: 1. If the image itself contains transparency, the ARGB_8888 format will still be used. 2. If it contains some rounded corners or Transform transformation about Alpha, The ARGB_8888 format will also be maintained.
  5. What part of the system’s memory does Glide occupy? Starting from Android 8.0, Bitmap memory is allocated in the Native heap. Before the hardware bitmap is turned on, a copy of the bitmap is also copied at the hardware layer, so it is double the memory. Starting from the Android O version, the hardware bitmap feature should be used with caution before you understand the disadvantages of hardware bitmaps. For details, please refer to: Understanding Hardware Bitmaps. As a result, the application will not cause OOM due to high image memory usage.

Some details used by Glide

When setting override(width, height) manually, be sure not to use a size larger than the actual size of the control.

Manually calling override has the highest priority for the size of the decoded image and will skip the sampling strategy calculation by default. If you clearly know how big the image is for the display area, you can use this method to avoid image enlargement (Article 5). If you want to use the original image size, you can use override(Target.SIZE_ORIGINAL).

Using preload to preload images will decode the original image size by default

There is no need to specify a Target for preload-related APIs. Glide will load the original image by default. If you want to specify the size required on the UI, you need to call the overloaded method preload(int width, int height) to customize the image. size.

In ImageView layout files, avoid setting unclear match_parent as width and height

Glide will not consider the scale_type attribute of ImageView when decoding images. Take a look at this example:

<ImageView
    android:id="@ + id/iv_cover_bg"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop" />

Since the image is set with the centerCrop attribute, the image will be centered and scaled according to the same ratio when viewed directly. However, since decoding occurs before scaleType takes effect, when loading the image using Glide, the image will be decoded with the width and height of the actual size of the ImageView (that is, full screen). This creates unnecessary memory overhead.

There are many solutions. Generally, you can calculate the actual image size that needs to be displayed through the original image and container aspect ratio in advance, and use the override(width, height) method to forcefully specify the size of the decoded image. Or if you know the proportion of the image, you can use layout_constraintDimensionRatio to constrain the size of the ImageView control.

If the cover image requires rounded corners, using the rounded corner attribute of CardView will have lower memory overhead than Glide rounderCorner.

The reason is that Glide rounderCorner occurs after image decoding, and a new bitmap will be generated after a round of transformation (the official description is that all bitmaps generated based on BitmapTransformation transformation will be stored in BitmapPool and eventually released according to the LRU algorithm), while CardView Through canvas.drawRoundRect(mBoundsF, mRadius, mRadius, paint), drawRoundRect will be converted into a series of drawing instructions to the GPU without causing additional memory overhead in the heap memory.

The following is experimental data, comparing memory data in three cases. The precondition is to load a 750×750 network image in jpeg format without transparency. Generally speaking, if you use ARGB_8888 format decoding, the memory overhead generated is 750x750x4 = 2197 kb. If you use RGB_565, you can reduce the memory overhead by half.

Scenario Native Heap(KB) Graphics (KB)
RGB_565 & Default right angle 12730 5410
RGB_565/ARGB_8888 & Rounded Corner Transform 17326 6564
RGB_565 & amp; CardView rounded corners 13633 5417

It can be seen that NativeHeap has been significantly reduced. It is speculated that it is related to the temporary overhead of bitmap caused by the additional transformation of RounderCorner. The reduction of Graphics by about 1M is in line with expectations.

Focus on the picture zoom scene

It is mentioned in the background knowledge that if the original image is larger than the control container, the image will be reduced and will not cause memory waste. But what happens if the original image is smaller than the container? For example: the original image is 100×100, but the container is 200×200, then by default a 200×200 bitmap will be decoded. In fact, Glide’s overall image scaling mechanism is completed through the internal DownsampleStrategy, which internally defines a sampling strategy similar to ScaleType. The default sampling strategy is CENTER_OUTSIDE: When When the original image size control size, the decoded image may be larger than the control size. Figure situation.

Usage example:

Glide.with(this)
    .asBitmap()
    .load(url)
    .apply(RequestOptions.downsampleOf(DownsampleStrategy.AT_LEAST))
    .into(imageView)

RGB_565 mode does not take effect and conflicts with Alpha Transform.

Effectively reduce memory. For apps with multiple images, texts or feed lists, turning on this configuration can reduce memory by 10-20M. Example to enable RGB_565 decoding configuration globally.

@GlideModule
class GlideConfig : AppGlideModule() {

    override fun applyOptions(context: Context, builder: GlideBuilder) {
        super.applyOptions(context, builder)
        builder.setDefaultRequestOptions(RequestOptions().format(DecodeFormat.PREFER_RGB_565))
    }
    ...
}

But at the same time you need to understand that in some cases RGB_565 may not take effect and even produce strange display effects.

  1. If the original image contains alpha transparency, the configuration will be invalid.
  2. When the RGB_565 configuration is used at the same time as the RoundedCorners/CircleCrop transformation, the configuration is invalid.
  3. When RGB_565 is used simultaneously with some transformations that contain hardware drawing instructions, the configuration fails or the display is abnormal.

1/2 is easier to understand. For 3, the common one is the Gaussian blur effect (frosted glass). This effect is currently through the RenderScript related API. This API requires that the application view must turn on hardware acceleration. In this case, turning on the RGB_565 configuration at the same time will cause exceptions, may return an empty bitmap, and may return an unexpected blur effect on some devices.

Image degradation is not necessarily related to memory optimization

According to the above Glide scaling processing of pictures, by default the maximum memory occupied by a picture is only related to the control width/height and decoding format.

During the entire image processing process, image degradation only reduces the image download speed and has no direct impact on memory usage.

In order to help everyone better master the relevant knowledge points of the open source framework, This is a study manual of the Android open source framework↓↓↓

If necessary, you can copy the link below and send it directly! ! !
https://qr21.cn/CaZQLo?BIZ=ECOMMERCE