图片加载和Cache
今天我和大家分享的是图片的加载以及图片的缓存,项目中我们运用过最多的是图片的加载。假如通过ImageView来显示图片,很多时候ImageView的大小并没有图片大,这样加载不但没有办法显示原始图片而且图片过大还容易出现OOM问题。如何高效的加载Bitmap图片?就是采用BitmapFactory.Options来加载所需的图片大小。
BitmapFactory.Options来缩放图片,主要是用到inSampleSize(采样率)参数。inSampleSize为1时,采样后的图片大小为图片的原始大小。inSampleSize为2时其图片的宽/高为原图大小的1/2。像素为原图的1/4。故缩放后图片的大小是以采样率2次方形式递减。
eg:以一张1024*768图片为例,采样率为:4
/**
* 方法名:decodeSampledBitmap(Resources res,int resId,int reqWidth,int reqHeight)<br>
* 用 法:用于获取压缩bitmap<br>
* 参 数:Resources res int resId(资源ID) int reqWidth(请求宽度) int reqHeight(请求高度)<br>
* 返回值:采样率<br>
*/
private Bitmap decodeSampledBitmap(Resources res,int resId,int reqWidth,int reqHeight){
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;//仅获取图片的长宽
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;//获取图片的长宽以及像素
return BitmapFactory.decodeResource(res, resId, options);//根据资源获取图片
}
/**
* 方法名:calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight)<br>
* 用 法:用于获取图片采样率<br>
* 参 数:BitmapFactory.Options options int reqWidth(请求宽度) int reqHeight(请求高度)<br>
* 返回值:采样率<br>
*/
public 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 halfHeight = height/2;
final int halfWidth = width/2;
while((halfHeight / inSampleSize) >= reqHeight && (halfWidth/inSampleSize) >= reqWidth){
inSampleSize *= 2;
}
}
return inSampleSize;
}
可见inSampleSize用于图片的压缩大大的减少了我们内存开销,以及流量开销(采样率也可用于加载网络图片压缩后续讲解到)
1.2图片的缓存策略
一般图片缓存我们运用较多的是LruCache(内存缓存),DiskLruCache(储存设备的缓存)。
LruCache是线程安全,且当缓存满时,移除较早使用的缓存对象。
LruCache初始化比较简单:
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);//当前进程的可用类存转化为KB
int cacheSize = maxMemory / 8;//大小为当前进程可用类存/8
mMemoryCache = new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {//重写sizeOf()返回当前图片的大小
return value.getRowBytes() * value.getHeight() /1024;
}
};
LruCache特殊情况下还需要重写LruCache的entryRemoved方法,LruCache移除旧缓存时会调用,此时可以在entryRemoved中完成一些资源的回收工作。
LruCache还支持获取和添加
mMemoryCache.get(key);
mMemoryCache.put(key, value)
用法和简单但是不影响它强大的功能,依靠LinkedHashMap支持访问顺序排序,淘汰最近使用最少缓存原则。
DiskLruCache初始化不能通过构造方法,而是使用它的open方法
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
File directory 文件存储路径
int appVersion app版本号,版本号改变DiskLruCache会清空之前所有的缓存文件
int valueCount 对应数据的个数,一般设为1。
long maxSize 缓存总数(eg:50M超过这个大小后DiskLruCache会清除一些缓存)
DiskLruCache 的写入:
private Bitmap loadBitmapFromHttp(String url,int reqWidth,int reqHeight){
if (Looper.myLooper() == Looper.getMainLooper()) {//这个方法不能在主线程中执行,会抛出异常
throw new IllegalArgumentException("Looper.myLooper() == Looper.getMainLooper()");
}
if (mDiskLruCache == null) {
return null;
}
String key = hashKeyFormUrl(url);//我们一般会用url作为key,但是有时url有特殊字符,故需要将url转为md5的值
Editor edit = mDiskLruCache.edit(key);//如果此时这个缓存正在编辑则返回null,diskLruCache不允许同时编辑一个缓存
if (edit != null) {
OutputStream newOutputStream = edit.newOutputStream(DISK_CACHE_INDEX);//因为前面我设置了对应的数据个数为1此时DISK_CACHE_INDEX为0即//可
if (downloadUrlToStream (url , newOutputStream)) {
edit.commit();//提交写入操作
}else{
edit.abort();//请求失败回退整个操作
}
mDiskLruCache.flush();
}
return loadBitmapFromDiskCache(url, reqWidth, reqHeight);
}
DiskLruCache 的缓存查找:
private Bitmap loadBitmapFromDiskCache(String url,int reqWidth,int reqHeight){
if (Looper.myLooper() == Looper.getMainLooper()) {
throw new IllegalArgumentException("Looper.myLooper() == Looper.getMainLooper()");
}
if (mDiskLruCache == null) {
return null;
}
Bitmap bitmap = null;
String key = hashKeyFormUrl(url);
Snapshot snapshot = mDiskLruCache.get(key);//通过key获得一个Snapshot对象
if (snapshot != null) {
FileInputStream fileInputStream = (FileInputStream)snapshot.getInputStream(DISK_CACHE_INDEX);//获得输入流
FileDescriptor fd = fileInputStream.getFD();
bitmap = mImageResizer.decodeSampleBitmapFromFileDescriptor(fd,reqWidth,reqHeight);
}
return bitmap;
图片的缓存Cache使用讲解结束了,如果读者想详细了解他们源码可上网查询。后面我会讲解一个具有图片的同步加载,图片的异步加载,图片压缩,磁盘缓存,网络拉取优秀的ImageLoad是怎么结合使用图片的Cache的。
谢谢品读!!