图片的三级缓存:内存,(本地)sd卡,网络,避免大图片造成oom(out of memory)
内存缓存也就是在程序维护键值对保存图片信息,程序退出后就不再存在
不管android内存多大,每个app会被分配固定的内存Runtime.getRuntime().maxMemory();获取分配大小
首先说明一下为什么要重写LruCache的sizeof方法,因为LruCache维护了一定的内存,他需要知道每个内存的大小
为什么内存缓存不使用软引用和弱引用呢?因为android2.3以后,垃圾回收器更倾向于回收软引用和弱引用对象
接口类:
public interface ImageCache {
public Bitmap get(String url);
public void put(String url,Bitmap bmp);
}
实现类:内存,sd和双缓存
//内存缓存类
public class MemoryCache implements ImageCache{
private LruCache<String,Bitmap> mMemeryCache;
//LruCache (此类在android-support-v4的包中提供) 。这个类非常适合用来缓存图片,
//它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使
//用的对象在缓存值达到预设定值之前从内存中移除。
public MemoryCache(){
//初始化LRU缓存
initImageCache();
}
private void initImageCache() {
//计算可使用最大内存
final int maxMemory=(int)(Runtime.getRuntime().maxMemory()/1024);
//取四分之一的可用内存作为缓存
final int cachsize=maxMemory/4;
// LruCache通过构造函数传入缓存值,以KB为单位。
mMemeryCache=new LruCache<String, Bitmap>(cachsize){
// 重写此方法来衡量每张图片的大小,默认返回图片数量
protected int sizeOf(String key, Bitmap value) {
/*
1、getRowBytes:Since API Level 1,用于计算位图每一行所占用的内存字节数。
2、getByteCount:Since API Level 12,用于计算位图所占用的内存字节数。
经实测发现:getByteCount() = getRowBytes() * getHeight(),也就是说位图所占用的内存空间数等于位图的每一行所占用的空间数乘以位图的行数。
因为getByteCount要求的API版本较高,因此对于使用较低版本的开发者,在计算位图所占空间时上面的方法或许有帮助。*/
//value.getRowBytes()单位字节value.getHeight单位px像素
return value.getRowBytes()*value.getHeight()/1024;
}
};
}
@Override
public Bitmap get(String url) {
return mMemeryCache.get(url);
}
@Override
public void put(String url, Bitmap bmp) {
mMemeryCache.put(url, bmp);
}
}
sd缓存实现
//sd卡缓存类
public class DiskCache implements ImageCache{
static String cacheDir="sdcard/cache/";
@Override
public Bitmap get(String url) {
//从本地文件中获取该图片
return BitmapFactory.decodeFile(cacheDir+url);
}
@Override
public void put(String url, Bitmap bmp) {
//将bitmap写入文件中
FileOutputStream fileOutputStream=null;
try {
fileOutputStream=new FileOutputStream(cacheDir+url);
//图片压缩
bmp.compress(CompressFormat.PNG, 100, fileOutputStream);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
CloseUtils.closeQuietly(fileOutputStream);
}
}
}
双缓存实现
//双缓存类
public class DoubleCache implements ImageCache{
ImageCache mMemoryCache=new MemoryCache();
ImageCache mDiskCache=new DiskCache();
//先从内存中获取图片,如果没有,再从sd卡中获取
@Override
public Bitmap get(String url) {
Bitmap bitmap=mMemoryCache.get(url);
if(bitmap==null){
bitmap=mDiskCache.get(url);
}
return bitmap;
}
//将图片缓存到内存和sd卡中
@Override
public void put(String url, Bitmap bmp) {
mMemoryCache.put(url, bmp);
mDiskCache.put(url, bmp);
}
}
辅助类
//统一关闭对象
public class CloseUtils {
private CloseUtils(){}
public static void closeQuietly(Closeable closeable){
if(null !=closeable){
try {
closeable.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
缓存操作类
public class ImageLoader {
//图片缓存 默认内存缓存
ImageCache mImageCache=new MemoryCache();
//线程池,线程数量为cpu数量
ExecutorService mExecutorService=Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
//注入缓存实现
public void setImageCache(ImageCache cache){
mImageCache=cache;
}
public void displayImage(String imageUrl,ImageView imageView){
Bitmap bitmap=mImageCache.get(imageUrl);
if(bitmap != null){
imageView.setImageBitmap(bitmap);
return;
}
//图片没缓存,提交到线程池去下载图片
submitLoadRequest(imageUrl, imageView);