Glide加载图片设置RBG565格式踩坑记录

987 阅读3分钟

背景

Android开发者使用Glide框架可以很方便地在App中加载各种类型(jpg、png等)、来源(网络、本地文件等)的图片,除此之外Glide自身还使用了二级缓存策略帮助开发者实现内存管理,使得程序员能更关注于业务能力的开发。但是即使如此,加载图片仍需要较大的内存,以Glide默认的加载格式ARGB8888为例,加载一张800px * 480px的小图片,需要消耗(800 * 480 * 4B) = 1.5M。因此我们需要一些方法降低图片占用的内存,其中将加载格式改RGB565时,同样尺寸的图片只需要要消耗一半的内存。

方法

Glide默认的加载格式是ARGB8888,开发者有两种方法修改成RGB565格式。

  1. 单次修改

    使用format()方法,指定为565格式

    Glide.with(this)
        .asBitmap()
        .load("xxx")
        .format(DecodeFormat.PREFER_RGB_565)
        .listener(object :RequestListener<Bitmap>{
            override fun onLoadFailed(
                e: GlideException?,
                model: Any?,
                target: Target<Bitmap>?,
                isFirstResource: Boolean
            ): Boolean {
                return false
            }
    ​
            override fun onResourceReady(
                resource: Bitmap?,
                model: Any?,
                target: Target<Bitmap>?,
                dataSource: DataSource?,
                isFirstResource: Boolean
            ): Boolean {
                Log.i("TAG",resource?.config?.toString()?:"")
            }
        })
        .into(imageView)
    
  1. 全局修改

    通过AppGlideModule添加全局设置

    @GlideModule
    public class OkHttpGlideModule extends AppGlideModule {
        private final RequestOptions requestOptions = new RequestOptions();
    ​
        @Override
        public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {         builder.setDefaultRequestOptions((RequestOptions)this.requestOptions.format(DecodeFormat.PREFER_RGB_565));
        }
    }
    

问题

运行程序后,我们发现有些图片加载后依旧打印出了ARGB8888,有些图片却是符合预期的RGB565。为什么会出现这样的情况,我们直接看源码:

/**
 * Bitmaps decoded from image formats that support and/or use alpha (some types of PNGs, GIFs etc)
 * should return {@link android.graphics.Bitmap.Config#ARGB_8888} for {@link
 * android.graphics.Bitmap#getConfig()}. Bitmaps decoded from formats that don't support or use
 * alpha should return {@link android.graphics.Bitmap.Config#RGB_565} for {@link
 * android.graphics.Bitmap#getConfig()}.
 *
 * <p>On Android O+, this format will use RGB_565 only when it's not possible to use {@link
 * android.graphics.Bitmap.Config#HARDWARE}.
 */
PREFER_RGB_565;

大致意思是,对于一些支持或者使用了Alpha通道的图片(比如png、gif等),返回ARGB8888;对于不支持或者没有使用Alpha通道的图片返回RGB565格式。其实我们从PREFER_RGB_565的名字上也能看出一些问题,是[Prefer]_rgb_565,并不是完全指定为rgb_565。

原因

找到Glide解码相关的代码

@SuppressWarnings("deprecation")
private void calculateConfig(
    ImageReader imageReader,
    DecodeFormat format,
    boolean isHardwareConfigAllowed,
    boolean isExifOrientationRequired,
    BitmapFactory.Options optionsWithScaling,
    int targetWidth,
    int targetHeight) {
​
  //第一部分硬件解码
  if (hardwareConfigState.setHardwareConfigIfAllowed(
      targetWidth,
      targetHeight,
      optionsWithScaling,
      isHardwareConfigAllowed,
      isExifOrientationRequired)) {
    return;
  }
​
  //第二部分指定为ARGB8888
  // Changing configs can cause skewing on 4.1, see issue #128.
  if (format == DecodeFormat.PREFER_ARGB_8888
      || Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) {
    optionsWithScaling.inPreferredConfig = Bitmap.Config.ARGB_8888;
    return;
  }
​
  boolean hasAlpha = false;
  try {
    hasAlpha = imageReader.getImageType().hasAlpha();
  } catch (IOException e) {
    if (Log.isLoggable(TAG, Log.DEBUG)) {
      Log.d(
          TAG,
          "Cannot determine whether the image has alpha or not from header"
              + ", format "
              + format,
          e);
    }
  }
​
  optionsWithScaling.inPreferredConfig =
      hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
  if (optionsWithScaling.inPreferredConfig == Config.RGB_565) {
    optionsWithScaling.inDither = true;
  }
}

整个方法分为三个判断分支:

  1. 如果支持硬件解码,这段代码与我们无关,暂且不看
  2. 如果设置了ARGB8888或者SDK为16时,则指定为ARGB_8888
  3. 如果有alpha通道,那么就设置为RGB565,否则依然是ARGB8888。

很明显,我们的问题就出现在第三个判断分支,部分图片使用了alpha通道,所以即使我们指定了RGB565的格式,Glide也会使用ARGB8888的格式。还是去看源码,DefaultImageHeaderParser#getType,Glide通过读取EXIF信息,判断图片属性,从而判断不同的图片格式。

图片格式