一、Bitmap 的加载方法
BitmapFactory 类提供了 4 类方法来加载 Bitmap:decodeFile()
、decodeResource()
、decodeStream()
、decodeByteArray()
,分别支持从文件系统、资源、输入流以及字节数组中加载出一个 Bitmap 对象。其中 decodeFile()、
decodeResource()间接调用了
decodeStream()`,这四类方法最终是在 Android 底层实现的,对应着 BitmapFactory 类的几个 native 方法。
二、Android 中的缓存策略
一般的图片加载策略都是:当程序第一次从网络加载图片后,就将其缓存到存储设备上,这样下次加载使用这张图片就不用再从网络上获取了。很多时候,为了提高用户体验,往往会把图片放在内存中缓存一份,这样应用打算从网络中获取图片的时候,首先从内存中获取,如果内存中没有就去存储设备中获取,如果存储设备也没有就去从网络中加载图片。
缓存策略主要包括:缓存的添加、获取、删除。目前使用比较普遍的缓存算法是:LRU(Least Recently Used 近期最少使用算法)。LRU 算法的核心思想是:当缓存满时,会优先淘汰那些近期最少使用的缓存对象。它的缓存有两种:LruCache(内存缓存)和 DiskLruCache(磁盘缓存)。
- Lrucache(内存缓存)
LruCache
类是一个泛型类,内部采用一个 LinkedHashMap 以强引用的方式存储外界的缓存对象。其提供了 get()
和 put()
方法来完成缓存对象的存取。当缓存满的时候,LruCache 会移除较早使用的缓存对象,然后添加新的缓存对象。
//内存缓存为进程最大可使用内存的 1/8,单位 KB
int maxMemory = (int)(Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
//单位相同,转成 KB
return value.getRowBytes() * value.getHeight() / 1024;
}
};
- DiskLruCache(磁盘缓存)
通过将缓存对象写进文件系统以实现缓存的效果。
1、DiskLruCache 的创建:
public static DiskLruCache open(File dictory,int appVersion,int valueCount,long maxSize)
2、DiskLruCache 的缓存添加:
DiskLruCache 的缓存添加操作是通过 Editor 完成的,Edtior 表示一个缓存对象的编辑对象。以图片加载为例,获取图片缓存时,首先需要根据图片的 url 的 md5 值作为 key 来通过 edit()
获取 Editor 对象。
/**
* 从网络中获取图片,存入磁盘缓存,然后加载到内存缓存中
* @param url
* @param reqWidth
* @param reqHeight
* @return
*/
private Bitmap loadBitmapFromHttp(String url,int reqWidth,int reqHeight) throws IOException{
//不能在主线程调用
if(Looper.myLooper() == Looper.getMainLooper()){
throw new RuntimeException("cannot visit network from UI Thread");
}
if(mDiskLruCache == null){
return null;
}
String key = hashKeyFromUrl(url);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if(editor != null){
OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
//存到磁盘缓存
if(downloadUrlToStream(url,outputStream)){
editor.commit();
}else {
editor.abort();
}
mDiskLruCache.flush();
}
return loadBitmapFromDiskCache(url,reqWidth,reqHeight);
}
3、DiskLruCache 的缓存查找
缓存查找过程也是通过将 url 转成 key,然后通过 DiskLruCache 的 get()
获取一个 SnapShot 对象,紧接着通过 SnapShot 对象即可获得缓存文件的输入流,然后通过输出流得到 Bitmap 对象。
/**
* 从磁盘缓存中加载Bitmap
* @param url
* @param reqWidth
* @param reqHeight
* @return
*/
private Bitmap loadBitmapFromDiskCache(String url,int reqWidth,int reqHeight) throws IOException{
//不能在主线程调用
if(Looper.myLooper() == Looper.getMainLooper()){
throw new RuntimeException("cannot visit network from UI Thread");
}
if(mDiskLruCache == null){
return null;
}
Bitmap bitmap = null;
String key = hashKeyFromUrl(url);
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
if (snapshot != null) {
FileInputStream fileInputStream = (FileInputStream)snapshot.getInputStream(DISK_CACHE_INDEX);
FileDescriptor fileDescriptor = fileInputStream.getFD();
bitmap = mImageResizer.decodeSampleBitmapFromFileDescriptor(fileDescriptor,reqWidth,reqHeight);
// 添加到内存缓存
if (bitmap != null){
addBitmapToMemoryCache(key,bitmap);
}
}
return bitmap;
}
三、自定义一个两级缓存的 ImageLoader
主要的思路:在加载图片的时候,首先判断是否有内存缓存,没有的话就去查找磁盘缓存,磁盘缓存仍然没有就去加载网络,然后将获取到的图片存储进磁盘缓存中,然后添加进内存缓存中。
网友评论