Bitmap的加载和Cache
Bitmap的高效加载
使用BitmapFactory
加载一张图片的方式
-
decodeFile
从文件 -
decodeResource
从资源 -
decodeStream
从输入流 -
decodeByteArray
从字节数组
核心思想
采用
BitmapFactory.Options
按照一定的采样率(inSampleSize
)来加载缩小后的图片,这样就会降低内存的占用避免OOM
,提高了Bitmap加载时的性能
inSampleSize = 1
,那么采样后的图片是原始图片的大小。inSampleSize大于1等于2时,采样后的图片的宽高均为原图的二分之一,像素数为原图的四分之一
//获取采样率
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会移除较早使用的缓存对象,然后再添加新的缓存对象。
- 强引用: 直接的对象引用;
- 软引用: 当一个对象只有软引用存在的时候,系统内存不足的时候,对象会被回收;
- 弱引用: 当一个对象只有弱引用存在的时候,对象随时可能被系统回收。
public class LruCache {
private final LinkedHashMap map;
// LruCache 内存缓存 初始化操作
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;//总容量的大小为当前最大内存的八分之一
mLruCache = new LruCache(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的创建
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可能含有特殊字符,所以一般采用url
的md5
值做为key
。
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对象即可获得缓存的文件输入流。
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.Options
对FileInputStream
进行缩放会出现问题,因为FileInputStream
是一种有序的文件流,两次decodeStream
调用会影响文件流的位置属性,导致第二次decodeStream
时得到的是null
。为了解决这个问题,可以通过文件流来得到它所对应的文件描述符,然后通过BitmapFactory.decodeFileDescriptor
来加载一张缩放后的图片。
- DiskLruCache.delete
- DiskLruCache.remove