使用三级缓存解决内存溢出

在Android开发的过程中,涉及到图片通常容易产生内存溢出的问题,

使用三级缓存的思路可以比较好的解决这个问题。

如下图所示为三级缓存的示意图,第一级为内存缓存,第二级为软引用缓存,第三级为文件缓存

它们的特点如下图所示。

image

 

类图如下图所示,Cache为缓存的接口,定义了缓存必须实现的方法,其他的类均实现了Cache接口。ThreeLevelCache包含有

MemoryCache、SoftReferenceCache和FileCache。

 

image

Cache接口

public interface Cache {
	
	public void put(String key,Bitmap bitmap);//存入
	
	public Bitmap get(String key);//取出
	
	public boolean isExist(String key);//是否存在
	
	public void clear();//清空

}

内存缓存MemoryCache

/**
 * 内存缓存,设置大小限制,当超过限制,将最近最久未使用的一项移出
 * @author huangbei
 *
 */
public class MemoryCache implements Cache {
	
	private  Map<String, Bitmap> cache ;
	
	private Cache softCache;
	
	//内存缓存所占的字节,初始为0
	private long size ;
	
	// 内存缓存当中所占字节的最大限制
	private long limit ;
	
	public MemoryCache(Cache softCache){
		//设置默认限制为最大运行内存的八分之一
		setLimit(Runtime.getRuntime().maxMemory() / 8);  
		size=0;
		this.softCache= softCache;
		// 放入缓存时是个同步操作  
		// LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近使用次数由少到多排列,即LRU  
		// 这样的好处是如果要将缓存中的元素替换,则先遍历出最近最少使用的元素来替换以提高效率  
		cache = Collections.synchronizedMap(new LinkedHashMap<String, Bitmap>(10, 1.5f, true));
	}
	
	public void setLimit(long limit){
		this.limit=limit;
	}

	@Override
	public void put(String key, Bitmap bitmap) {
		this.cache.put(key, bitmap);
		if(!checkSize(bitmap)){
			remove();
		}
	}

	@Override
	public Bitmap get(String key) {
		return cache.get(key);
	}

	@Override
	public boolean isExist(String key) {
		return cache.containsKey(key);
	}

	@Override
	public void clear() {
		cache.clear(); 
	}
	
	//检查添加的图片是否会超过限制
	private boolean checkSize(Bitmap bitmap){
		long bitsize=BitmapUtil.getSizeInBytes(bitmap);
		if((size+bitsize)<=limit){
			return true;
		}else{
			return false;
		}
	}
	
	//移除掉超过内存限制的图片
	private void remove(){
		Iterator<Entry<String, Bitmap>> iter = cache.entrySet().iterator();  
		while (iter.hasNext()) {  
			Entry<String, Bitmap> entry = iter.next();  
			size -= BitmapUtil.getSizeInBytes(entry.getValue());
			softCache.put(entry.getKey(), entry.getValue());
			iter.remove();
			if (size <= limit)  
			break;  
		}
	}

}

软引用缓存SoftReferenceCache

/**
 * 软引用缓存,不设置大小限制,内存不够,系统自动释放
 * @author huangbei
 *
 */
public class SoftReferenceCache implements Cache {
	
	//软引用缓存
	private  Map<String, SoftReference<Bitmap>> cache;
	
	public SoftReferenceCache(){
		cache = new HashMap<String, SoftReference<Bitmap>>();
	}

	@Override
	public void put(String key, Bitmap bitmap) {
		cache.put(key, new SoftReference<Bitmap>(bitmap));
	}

	@Override
	public Bitmap get(String key) {
		return cache.get(key).get();
	}

	@Override
	public boolean isExist(String key) {
		if(cache.containsKey(key)&&cache.get(key).get()!=null)
			return true;
		else
			return false;
	}

	@Override
	public void clear() {
		cache.clear(); 
	}

}

文件缓存FileCache

/**
 * 文件缓存,图片存于文件当中,不设置大小限制,避免重复下载,节省流量
 * @author huangbei
 *
 */
public class FileCache implements Cache{
	
    private String cashe_dir="";
    
    private File cacheDir;  //图片存储的路径
    
    public FileCache(Context context) {
        if (android.os.Environment.getExternalStorageState()
                .equals(android.os.Environment.MEDIA_MOUNTED)){
             cacheDir = new File(android.os.Environment.getExternalStorageDirectory(), cashe_dir);
        }else{
            cacheDir = context.getCacheDir();
        }
        if (!cacheDir.exists())
            cacheDir.mkdirs();
    }

	@Override
	public void put(String key, Bitmap bitmap) {
		File f = new File(cacheDir, key);
        if(!f.exists()){
            try {
                f.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        BitmapUtil.saveBitmap(f, bitmap);
	}

	@Override
	public Bitmap get(String key) {
		File f = new File(cacheDir, key);
        if (f.exists()){
            return BitmapUtil.decodeFile(f);
        }else{
        	return null;
        } 
	}

	@Override
	public boolean isExist(String key) {
		File f = new File(cacheDir, key);
        if (f.exists()){
            return true;
        }else{
        	return false;
        } 
	}

	@Override
	public void clear() {
		File[] files = cacheDir.listFiles();
        for (File f : files)
            f.delete();
	}

}

三级缓存 ThreeLevelCache

public class ThreeLevelCache implements Cache{
	
	private Cache memoryCache;
	private Cache softCache;
	private Cache fileCache;
	
	private ThreeLevelCache cache; 
	
	private ThreeLevelCache(Context context){
		this.softCache=new SoftReferenceCache();
		this.fileCache=new FileCache(context);
		this.memoryCache=new MemoryCache(softCache);
	}
	
	public ThreeLevelCache newInstance(Context context){
		if(cache==null){
		    cache=new ThreeLevelCache(context);
		}
		return cache;
	}

	@Override
	public void put(String key, Bitmap bitmap) {
        this.fileCache.put(key, bitmap);
        this.memoryCache.put(key, bitmap);
	}

	@Override
	public Bitmap get(String key) {
		if(memoryCache.isExist(key)){
			return memoryCache.get(key);
		}else if(softCache.isExist(key)){
			return softCache.get(key);
		}else if(fileCache.isExist(key)){
			return fileCache.get(key);
		}else{
			return null;
		}
	}

	@Override
	public boolean isExist(String key) {
		return memoryCache.isExist(key)||softCache.isExist(key)||fileCache.isExist(key);
	}

	@Override
	public void clear() {
		memoryCache.clear();
		softCache.clear();
		fileCache.clear();
	}
}

其中所用到的一些关于图片的方法存于类BitmapUtil中

public class BitmapUtil {
	
	//图片所占的内存大小
	public static long getSizeInBytes(Bitmap bitmap) {  
		if (bitmap == null){ 
			return 0;  
		}
		return bitmap.getRowBytes() * bitmap.getHeight();  
	}
	
	//将图片存入文件当中
	public static boolean saveBitmap(File file, Bitmap bitmap){
        if(file == null || bitmap == null)
            return false;
        try {
            BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
            return bitmap.compress(CompressFormat.JPEG, 100, out);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        }
    }
	
	// decode这个图片并且按比例缩放以减少内存消耗,虚拟机对每张图片的缓存大小也是有限制的  
	public static Bitmap decodeFile(File f) {  
	    try {  
		    // decode image size  
		    BitmapFactory.Options option = new BitmapFactory.Options();  
		    option.inJustDecodeBounds = true;  
		    BitmapFactory.decodeStream(new FileInputStream(f), null, option);  
		    // Find the correct scale value. It should be the power of 2.  
		    final int REQUIRED_SIZE = 100;  
		    int width_tmp = option.outWidth, height_tmp = option.outHeight;  
		    int scale = 1;  
		    while (true) {  
			    if (width_tmp / 2 < REQUIRED_SIZE  || height_tmp / 2 < REQUIRED_SIZE){
			    	break;  
			    }
			    width_tmp /= 2;  
			    height_tmp /= 2;  
			    scale *= 2;  
		    }  
		    // decode with inSampleSize  
		    BitmapFactory.Options option2 = new BitmapFactory.Options();  
		    option2.inSampleSize = scale;  
		    return BitmapFactory.decodeStream(new FileInputStream(f), null, option2);  
	    } catch (FileNotFoundException e) {  
	    	e.printStackTrace();
	    	return null;
	    }  
	}

}
posted @ 2016-05-19 17:40  黄大仙爱编程  阅读(407)  评论(0编辑  收藏  举报