十二、Bitmap的加载和Cache

Bitmap的加载和Cache,目前比较常用的缓存策略是LruCach和DisLruCache,其中LruCache常被用作内存缓存,而DisLruCache常被用做存储缓存。
Lru是Least Recently Used即最近最少使用算法,这种算法的核心思想是,当缓存快满时,会淘汰近期最少使用的缓存目标。

1.Bitmap的高效加载

加载图片,BitmapFactory类提供了四类方法:
decodeFile,decodeResource,decodeStream,decodeByteArray,分别用于从文件系统,资源,输入流,以及字节数组中加载出一个Bitmap对象,其中decodeFile和decodeResource又间接调用了decodeStream方法。

如何高效的加载图片呢?
核心思想就是采用BitmapFactory.Options来加载所需尺寸的图片。通过BitmapFactory.Options来缩放图片,主要是用到了它的inSampleSize参数,即采样率。

当inSampleSize为1时,采样后的图片大小为图片的原始大小,当inSampleSize大于1时,比如为2,那么采样后的图片其宽高均为原图大小的1/2,而像素为原图的1/4,其占用的内存大小也为原图的1/4.

如一张1024*1024像素的图,采用ARGB8888格式存储,那么它占用的内存为1024*1024*4,即4M,如果inSampleSize为2,那么采样后占用的内存为512*512*4,即1M。inSampleSize的缩放比例为(1/inSampleSize)2。

实际情况:
如果ImageView的大小为100*100像素,而图片的原始大小为200*200,那么只需要将采样率inSampleSize设置为2即可。如果图片为200*300呢,采样率还是设置为2,这样缩放后的图片大小为100*150,仍然是适合的。如果采样率为3,那么缩放后的图片大小就会小于ImageView所期望的大小,这样图片就会被拉伸导致模糊。

通过采样率可以高效的加载图片,如何获取采样率呢?
(1)将BitmapFactory.Options的inJustDecodeBounds参数设置为true并加载图片;
(2)从BitmapFactory.Options中取出图片的原始宽高信息,它们对应于outWidth和outHeight参数;
(3)根据采样率的规则并结合目标View的所需大小计算采样率inSampleSize;
(4);将BitmapFactory.Options的inJustDecodeBounds参数设置为false并重新加载图片;

注意:inJustDecodeBounds这个参数为true时,BitmapFactory只会解析图片的原始宽高信息,并不会真正地加载图片,所以这个操作是轻量级的。
另外需要注意的是,这个时候BitmapFactory获取的图片宽高信息和图片的位置以及程序运行的设备有关,比如同一张图片放在不同的Drawable目录下或者程序运行在不同屏幕密度的设备上,都可能导致BitmapFactory获取到不同的结果。

public class ImageResizer {
    private static final String TAG = "ImageResizer";
    public ImageResizer() {
    }
    public Bitmap decodeSampledBitmapFromResource(Resources res,
            int resId, int reqWidth, int reqHeight) {
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);
        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth,
                reqHeight);
        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }
    public Bitmap decodeSampledBitmapFromFileDescriptor(FileDescriptor fd, int reqWidth, int reqHeight) {
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFileDescriptor(fd, null, options);
        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth,
                reqHeight);
        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFileDescriptor(fd, null, options);
    }
    public int calculateInSampleSize(BitmapFactory.Options options,
            int reqWidth, int reqHeight) {
        if (reqWidth == 0 || reqHeight == 0) {
            return 1;
        }
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        Log.d(TAG, "origin, w= " + width + " h=" + height);
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
            // Calculate the largest inSampleSize value that is a power of 2 and
            // keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) >= reqHeight
                    && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }
        Log.d(TAG, "sampleSize:" + inSampleSize);
        return inSampleSize;
    }
}

2.Android中的缓存策略

2.1.LruCache

LruCache是一个泛型类,它内部采用一个LinkedHashMap以强引用的方式存储外界缓存对象,其提供了get和put方法来完成缓存的获取和添加操作,当缓存满时,LruCache会移除较早使用的缓存对象,然后再添加新的缓存对象。
强引用:直接的对象引用;
软引用:当一个对象只有软引用存在时,系统内存不足时此对象会被gc回收;
弱引用:当一个对象只有软引用存在时,此对象会随时被gc回收;
另外LruCache是线程安全的。
经典的初始化代码:

        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };

sizeof方法的作用是计算缓存对象的大小,这里大小的单位需要和总容量的单位一致。上面总容量为当前进程可用内存的1/8,单位为KB,sizeof方法则完成了Bitmap对象的大小计算。除以1024也是把单位转换为KB。

2.2.DiskLruCache

2.3.ImageLoader的实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值