强制将Glide加载图片格式换成RGB_565

3,528 阅读2分钟

由于公司APP主要是图片类应用,内存占用一直居高不下,特别是首页,全是用Glide加载的在线图,故尝试通过修改图片格式来降低内存:对于ARGB_8888的Bitmap,一个像素点存放了Alpha、Red、Green、Blue四种信息,总共4个字节,而若对透明通道没有要求,则使用RGB_565格式的,其占用2个字节,故对比起来可以节省一半的内存,代价是会损失一定的三色彩。

具体代码如下:

1.增加GlideApp配置

@GlideModule
public class AppGlide extends AppGlideModule {
    @Override
    public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
            builder.setDefaultRequestOptions(GlideRGB565DecodeUtil.getRequestOption());
    }
}

2.反射调用强行将ARGB_8888改为RGB_565

public class GlideRGB565DecodeUtil {
    private static Field bmpInBitmapResource;
    private static Field bmpPoolInBitmapResource;

    /**
     * 参考 {@link com.bumptech.glide.request.BaseRequestOptions#transform(Transformation, boolean)}
     */
    public static RequestOptions getRequestOption() {
        Transformation<Bitmap> transformation = new Transformation<Bitmap>() {
            @NonNull
            @Override
            public Resource<Bitmap> transform(@NonNull Context context, @NonNull Resource<Bitmap> resource, int outWidth, int outHeight) {
                Bitmap bitmap = resource.get();
                if (null == bitmap)
                    return resource;

                if (bitmap.getConfig() == Bitmap.Config.RGB_565) {
                    // 本身就是RGB_565的
                    return resource;
                }
                try {
                    if (null == bmpPoolInBitmapResource) {
                        bmpPoolInBitmapResource = BitmapResource.class.getDeclaredField("bitmapPool");
                        bmpPoolInBitmapResource.setAccessible(true);
                    }

                    if (null == bmpInBitmapResource) {
                        bmpInBitmapResource = BitmapResource.class.getDeclaredField("bitmap");
                        bmpInBitmapResource.setAccessible(true);
                    }

                    // 1.通过原图生成565的图
                    BitmapPool bitmapPool = (BitmapPool) bmpPoolInBitmapResource.get(resource);
                    Bitmap bitmap565 = bitmapPool.get(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.RGB_565);
                    Canvas canvas = new Canvas(bitmap565);
                    // 设置背景色,防止透明图出现黑色的情况
                    canvas.drawColor(Color.WHITE);
                    canvas.drawBitmap(bitmap, 0, 0, null);

                    // 2.使用565的图替换原图
                    bmpInBitmapResource.set(resource, bitmap565);

                    bitmapPool.put(bitmap);

                } catch (Exception ignore) { }
                return resource;
            }

            @Override
            public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
            }
        };
        Transformation<Bitmap> gifTransformation = new Transformation<Bitmap>() {
            @NonNull
            @Override
            public Resource<Bitmap> transform(@NonNull Context context, @NonNull Resource<Bitmap> resource, int outWidth, int outHeight) {
                return resource;
            }

            @Override
            public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { }
        };
        DrawableTransformation drawableTransformation =
                new DrawableTransformation(transformation, true);
        return new RequestOptions().transform(Bitmap.class, transformation)
                .transform(Drawable.class, drawableTransformation)
                .transform(BitmapDrawable.class, drawableTransformation.asBitmapDrawable())
                // GIF不能用RGB565加载,否则会出现背景白色问题
                .transform(GifDrawable.class, new GifDrawableTransformation(gifTransformation))
                .format(DecodeFormat.PREFER_RGB_565);
    }

    public static RequestOptions getARGBRequestOption() {
        return new RequestOptions().dontTransform()
                .format(DecodeFormat.PREFER_ARGB_8888);
    }

这样做之后便所有通过Glide加载之后的图都变成RGB_565的了,若有特殊要求需要透明的,则可如下调用即可:

            GlideApp.with(context)
                    .asBitmap()
                    .apply(GlideRGB565DecodeUtil.getARGBRequestOption())
                    .load(target)
                    .into(imageView);