sunxin's Studio.

Bitmap的加载与缓存策略

字数统计: 1.2k阅读时长: 4 min
2018/12/08 Share

Bitmap的加载和Cache

Bitmap的高效加载

使用BitmapFactory加载一张图片的方式

  • decodeFile 从文件
  • decodeResource 从资源
  • decodeStream 从输入流
  • decodeByteArray 从字节数组
    核心思想

    采用BitmapFactory.Options按照一定的采样率(inSampleSize)来加载缩小后的图片,这样就会降低内存的占用避免OOM,提高了Bitmap加载时的性能
    inSampleSize = 1 ,那么采样后的图片是原始图片的大小。inSampleSize大于1等于2时,采样后的图片的宽高均为原图的二分之一,像素数为原图的四分之一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//获取采样率
public static Bitmap decodeFile(String path) {
int finalWidth = 800;//规定宽度如果为800
//先获取宽度
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;//图片不加载到内存
BitmapFactory.decodeFile(path,options);
//取出图片的原始宽度
int outWidth = options.outWidth;
int inSampleSize = 1;
if (outWidth>finalWidth){
inSampleSize = outWidth/finalWidth;
}
//设置回去
options.inSampleSize = inSampleSize;
options.inJustDecodeBounds = false;


return BitmapFactory.decodeFile(path,options);
}

Android中的缓存策略

避免过多的流量消耗需要进行缓存,当程序第一次从网络加载图片后,将其缓存到存储设备上,下次使用的时候就不必再从网络拉取,为了提高用户体验,往往还会降图片再在内存中缓存一份,这样当应用打算从网络请求一张图片的时候,会先从内存中获取,内存没有就从存储设备获取,存储设备没有就在从网络上拉取。

缓存策略LRU(Least Recently Userd)算法: 最近最少使用算法,当缓存满时,会优先淘汰那些近期最少使用的缓存对象。
LruCache用来实现内存缓存,LruDiskCache用来实现存储设备缓存。二者可以完美结合。

LruCache

LruCache: 是一个泛型类,是线程安全的。内部采用LinkedHashMap以强引用的方式存储外界的缓存对象,提供了get和put方法来完成缓存的获取和添加操作,当缓存满时,LruCache会移除较早使用的缓存对象,然后再添加新的缓存对象。

  • 强引用: 直接的对象引用;
  • 软引用: 当一个对象只有软引用存在的时候,系统内存不足的时候,对象会被回收;
  • 弱引用: 当一个对象只有弱引用存在的时候,对象随时可能被系统回收。
1
2
public class LruCache<K, V> {
private final LinkedHashMap<K, V> map;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// LruCache 内存缓存 初始化操作
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;//总容量的大小为当前最大内存的八分之一

mLruCache = new LruCache<String, Bitmap>(cacheSize) {
//计算缓存对象的大小
@Override
protected int sizeOf(String key, Bitmap bitmap) {
//注意单位的转换 bytes --> kb
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}

//移除旧缓存时调用
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
// TODO: 2017/9/1 资源回收的工作
}
};

DiskLruCache

DiskLruCache:磁盘缓存,通过将缓存对象写入文件系统从而实现缓存效果

  • DiskLruCache的创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;//磁盘缓存50M大小

File diskCacheDir = getExternalCacheDir();
if (!diskCacheDir.exists()){
diskCacheDir.mkdirs();
}
/**
* Opens the cache in {@code directory}, creating a cache if none exists
* there.
*
* @param directory a writable directory 缓存目录
* @param appVersion 应用版本号
* @param valueCount the number of values per cache entry. Must be positive.
* @param maxSize the maximum number of bytes this cache should use to store
* @throws IOException if reading or writing the cache directory fails
*/
DiskLruCache diskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
  • DiskLruCache的缓存添加
    DiskLruCache.Editor:表示一个缓存对象的编辑对象。最后记得commit()
    注意:因为图片的url可能含有特殊字符,所以一般采用urlmd5值做为key
1
2
3
4
5
6
7
8
9
String imgUrl = "";
String key = MD5Util.getMd5Value(imgUrl);
DiskLruCache.Editor editor = diskLruCache.edit(key);
if (editor != null) {
editor.getString(0);
}
editor.commit();//提交
editor.abort();//回退
diskLruCache.flush();//刷新
  • DiskLruCache的缓存查找

    通过DiskLruCache.get获取一个SnapShot对象,再使用Snapshot对象即可获得缓存的文件输入流。

1
2
3
4
5
6
7
8
9
10
11
12
String key = MD5Util.getMd5Value(imgUrl);
Bitmap bitmap = null;
DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
if (snapshot != null) {
FileInputStream fileInputStream = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
//从文件流中获取文件描述符
FileDescriptor fileDescriptor = fileInputStream.getFD();
bitmap = decodeSampledBitmapFromFileDescriptor(fileDescriptor,reqWidth,reqHeight);
if (bitmap != null) {
addBitmapToMemoryCache(key,bitmap);
}
}

FileDescriptor:文件描述符

直接使用BitmapFactory.OptionsFileInputStream进行缩放会出现问题,因为FileInputStream是一种有序的文件流,两次decodeStream调用会影响文件流的位置属性,导致第二次decodeStream时得到的是null。为了解决这个问题,可以通过文件流来得到它所对应的文件描述符,然后通过BitmapFactory.decodeFileDescriptor来加载一张缩放后的图片。

  • DiskLruCache.delete
  • DiskLruCache.remove

自定义ImageLoader的实现与图片墙效果

CATALOG
  1. 1. Bitmap的加载和Cache
    1. 1.1. Bitmap的高效加载
    2. 1.2. Android中的缓存策略
      1. 1.2.1. LruCache
      2. 1.2.2. DiskLruCache
    3. 1.3. 自定义ImageLoader的实现与图片墙效果