背景
Android开发者使用Glide框架可以很方便地在App中加载各种类型(jpg、png等)、来源(网络、本地文件等)的图片,除此之外Glide自身还使用了二级缓存策略帮助开发者实现内存管理,使得程序员能更关注于业务能力的开发。但是即使如此,加载图片仍需要较大的内存,以Glide默认的加载格式ARGB8888为例,加载一张800px * 480px的小图片,需要消耗(800 * 480 * 4B) = 1.5M。因此我们需要一些方法降低图片占用的内存,其中将加载格式改RGB565时,同样尺寸的图片只需要要消耗一半的内存。
方法
Glide默认的加载格式是ARGB8888,开发者有两种方法修改成RGB565格式。
-
单次修改
使用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)
-
全局修改
通过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;
}
}
整个方法分为三个判断分支:
- 如果支持硬件解码,这段代码与我们无关,暂且不看
- 如果设置了ARGB8888或者SDK为16时,则指定为ARGB_8888
- 如果有alpha通道,那么就设置为RGB565,否则依然是ARGB8888。
很明显,我们的问题就出现在第三个判断分支,部分图片使用了alpha通道,所以即使我们指定了RGB565的格式,Glide也会使用ARGB8888的格式。还是去看源码,DefaultImageHeaderParser#getType,Glide通过读取EXIF信息,判断图片属性,从而判断不同的图片格式。