Android开发艺术探索 - 第12章 Bitmap的加载和cache

1.Bitmap高效加载

加载Bitmap的方法:使用BitmapFactory的decodeFile/decodeResource/decodeStream/decodeByteArray可以分别从,文件/资源/输入流/字节数组中加载一个Bitmap。decodeFile/decodeResource会间接调用decodeStream。

通过采样率控制加载出的Bitmap的大小,以提高加载效率:

  • 将BitmapFactory.Options的inJustDecodeBounds参数设置为true,并加载图片
  • 从BitmapFactory.Options中取出图片的原始宽高信息,即outWidth和outHeight
  • 根据采样率的规则(2的倍数)以及目标View的大小,计算出inSampleSize的大小
  • 将BitmapFactory.Options的inJustDecodeBounds参数设置为false,重新加载图片
public static Bitmap decodeSampleBitmapFromResource(Resources resource, int resId,
                                                    int reqWidth, int reqHeight) {
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(resource, resId, options);

    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(resource, resId, options);
}
public static int calculateInSampleSize (BitmapFactory.Options options, int reqWidth, int reqHeight) {
    int width = options.outWidth;
    int height = options.outHeight;
    int inSampleSize = 1;
    
    if (height > reqHeight || width > reqWidth) {
        int halfWidth = width / 2;
        int halfHeight = height / 2;
        
        while (halfHeight / inSampleSize >= reqHeight
                && halfWidth / inSampleSize >= reqWidth) {
            inSampleSize *= 2;
        }
    }
    
    return inSampleSize;
}

2.Android中的缓存策略

LRU(Least Recently Used):内存缓存LruCache和存储设备缓存DiskLruCache

  1. LruCache
    内部采用一个LinkedHashMap,以强引用的方式存储外界的缓存对象。使用方法,设置缓存大小,然后复写sizeOf计算缓存对象的大小,之后通过put和get方法,添加和获取缓存对象:
int cacheSize = 4 * 1024 * 1024; // 4MiB
LruCache bitmapCache = new LruCache(cacheSize) {
    protected int sizeOf(String key, Bitmap value) {
        return value.getByteCount();
    }
}}

如果缓存对象引用的资源需要显式的进行释放,可以复写entryRemoved方法释放资源。
当通过get方法成功访问到一个缓存对象,在LruCache内部会将其移动到队列头部;通过put方法添加缓存时,如果缓存已满,队列最后一个对象就会被删除。可以通过remove方法直接删除一个缓存对象。
LruCache是线程安全的。
2. DiskLruCache
将缓存对象写入文件系统进行缓存。不在sdk中,需另行下载。源码

  • 创建过程
    通过DiskLruCache#open方法进行
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
        throws IOException {

参数:

  • directory:缓存目录
  • appVersion:应用版本号,版本号改变时会清空之前的缓存
  • valueCount:单个key对应的数据个数
  • maxSize:缓存空间大小
private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;
File diskCacheDir = getDiskCacheDir(mContext, "bitmap");
if (!diskCacheDir.exists()) {
    diskCacheDir.mkdirs();
} 
mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
  • 添加缓存
    DiskLruCache的缓存添加操作,通过Editor来完成。通过Editor得到一个输出流:
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null) {
    OutputStream out = editor.newOutputStream(0);   // valueCount为1,index从0开始
}

将数据写入到该输出流之后,调用editor的commit方法,缓存创建完成:

if (downloadUrlToStream(url, outputStream)) {
    editor.commit();
}
  • 获取缓存
    DiskLruCache的get方法获取一个Snapshot对象,通过它便可得到输入流,通过该输入流便可得到缓存的文件。对于缓存Bitmap时的处理:因为在加载大图的时候,需要decode两次,而输入流读取过一次,第二次就会返回null,对于这种情况,可以通过输入流获得文件的文件描述,然后调用BitmapFactory.decodeFileDescriptor来加载Bitmap:
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
if (snapshot != null) {
    FileInputStream in = (FileInputStream) snapshot.getInputStream(0);
    FileDescriptor fd = in.getFD();

    bitmap = decodeSampleBitmapFromFD(fd, reqWidth, reqHeight);
    if (bitmap != null) {
        addBitmapToMemoryCache(key, bitmap);
    }
}

你可能感兴趣的:(编程,Android,Java)