《性能优化: 图片加载优化》

加载图片

加载图片的来源可能是手机本地磁盘、网络资源或者是资源文件中的图片。图片分辨率不一、或大或小,但最终图片显示在屏幕上都是Bitmap的形式,一张图片被加载到内存当中所占据的内存大小,除了和图片分辨率有关还和格式有关。因为格式不同,每个像素点占据的内存空间就不一样,通常的格式有ARGB_8888和RGB_565。前者每个像素占用4个字节,后则占用两个字节

图片大小计算

A图:分辨率为300*520、格式为ARGB_8888.
300 * 520 * 4 = 0.595M

B图:一张分辨率为2048*1500、格式为ARGB_8888
2048 * 1500 * 4 = 11.72M

假如A图和B图显示的内容一样,现在一个ImageView宽高非常小,那么我们优先加载A图,因为没有必要加载一张大图显示到宽高比较小的控件上,加载B图,不仅浪费内存资源,像ListView,GridView,如果都是加载大图,有可能造成OOM异常。

优化加载

加载一些未知来源的图片,由于我不知道图片的宽高大小和形状,加载的图片很有可能超出我需要显示的大小,所以在加载前对它的大小做一下检测,如果图片的大小(分辨率)远超出了我们需要现实的大小,我们需要对图片进行压缩处理,这是在客户端处理未知来源图片的优化方式,而服务端(自己公司的图片服务器)一般根据请求的url时带上不同的参数直接获取需要不同等级或者不同分辨率的图片。所以优化的手段有两种,一种是服务对图片进行压缩处理再返回,第二种就是客户处理。核心就是对图片进行压缩优化

服务端压缩优化
举两个例子

1、阿里云图片服务器
阿里云图片服务器一般在请求图片url带上你期望的图片分辨率参数,服务器会更具给定的分辨率给压缩图片返回给客户端。

2、豆瓣电影图片
豆瓣电影图片请求url中 更改图片等级l、m、s就会返回相应等级的图片大小。

https://img1.doubanio.com/view/photo/l/public/p2529206747.jpg
https://img1.doubanio.com/view/photo/m/public/p2529206747.jpg
https://img1.doubanio.com/view/photo/s/public/p2529206747.jpg

服务端具体是怎么压缩处理的,不是我们关注的重点,移动端才是我们所需要研究的。

客户端压缩优化

我们用BitmapFactory去加载一张图片生成Bitmap对象,根据不同的图片资源提供了不同的方法decodeFile用于加载SD卡中的图片、decodeStream用于加载网络中的图片、decodeResource用于加载资源中图片,这些方法会尝试为已经构建的bitmap分配内存,如果此时内存资源紧缺会很容易导致OOM异常。为此每一种解析方法都提供了一个可选的BitmapFactory.Options参数,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存,返回值也不再是一个Bitmap对象,而是null。虽然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType属性都会被赋值。这个技巧让我们可以在加载图片之前就获取到图片的长宽值和MIME类型,从而根据情况对图片进行压缩。如下代码所示:

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

BitmapFactory.decodeResource(getResources(), R.id.myimage, options);

int imageHeight = options.outHeight;

int imageWidth = options.outWidth;

String imageType = options.outMimeType;

而压缩图片可以通过BitmapFactory.Options中inSampleSize
来实现。

压缩思路

1、获取原图分辨率
2、获取显示的ImageView的显示分辨率
3、计算压缩size
4、设置inSampleSize = size

关于inSampleSzie:我们有一张20481536像素的图片,将inSampleSize的值设置为4(即缩小为原来的四分之一),就可以把这张图片压缩成512384像素。原本加载这张图片需要占用13M的内存,压缩后就只需要占用0.75M了

直接套用郭霖大神的方法
参考https://blog.csdn.net/guolin_blog/article/details/9316683
计算压缩size

public static int calculateInSampleSize(BitmapFactory.Options options,
        int reqWidth, int reqHeight) {
    // 源图片的高度和宽度
    final int height = options.outHeight;
    final int width = options.outWidth;

    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        // 计算出实际宽高和目标宽高的比率
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
        // 一定都会大于等于目标的宽和高。
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }
    return inSampleSize;
}

获取压缩后的Bitmap


public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {
    // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
    final BitmapFactory.Options options = new BitmapFactory.Options();

    options.inJustDecodeBounds = true;

    BitmapFactory.decodeResource(res, resId, options);

    // 调用上面定义的方法计算inSampleSize值
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // 使用获取到的inSampleSize值再次解析图片
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

下面的代码非常简单地将任意一张图片压缩成100*100的缩略图,并在ImageView上展示。

mImageView.setImageBitmap(
   decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

Glide图片加载框架

Glide是现在非常流行的图片缓存框架,也是Google官方推荐使用的。它加载网络、磁盘和资源中的图片,内置四级缓存优化、并且会根据ImageView的大小进行图片的压缩处理,它与当前的Activity或Fragment保持相同的声明周期。

关于Glide更多使用 Please look 《图片缓存框架-Glide(使用到源码解析博客整理)》

你可能感兴趣的:(《性能优化: 图片加载优化》)