当要加载网络图片时使用缓存能提高性能又能减少服务器的压力。为了兼顾速度与缓存图片的数量应使用多级缓存
1传入url从LinkedHashMap获取图片,LinkedHashMap可以采用LRU算法按顺序插入元素,这样LinkedHashMap可以只保存最近使用的N个元素。
2如果hashmap没有此图片则从 ConcurrentHashMap>获取图片。这里使用了SoftReference(软引用) 如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。软引用与LinkedHashMap配合使用,当LinkedHashMap插入元素时,如果LinkedHashMap的元素数目大于某个值,则把这个元素插入 ConcurrentHashMap>中,而不是插入LinkedHashMap中。这样保证了速度又使得缓存图片的数量尽量的大。
3如果 ConcurrentHashMap>没有找到这个图片,则从sd卡中寻找,找到则插入到LinkedHashMap中。
4如果sd卡中没有图片,则从网络中下载图片,并把图片保存在sd卡以及插入到LinkedHashMap中。
public class CacheBitmap { //图片所在的文件夹 private final String imgDir=Tool.getSDPath()+File.separator+"weixun"+File.separator+"img"; //本地最多缓存200张图片 private final int maxImgCount=200; //sdcard的剩余容量至少为minSDCacheSize private final int minSDCacheSize=2; //内存缓存的图片数量 private final int cacheSize=20; private final int mb=1024*1024; private static CacheBitmap instance; private static final String TAG="common.CacheBitmap"; private CacheBitmap() { } public static CacheBitmap getInstance() { if(instance==null) { instance=new CacheBitmap(); } return instance; } /* *返回图片文件 * *@param url 图片url地址 *@return */ public Bitmap getBitmap(String url) { if (url.equals("")) { return null; } else { Bitmap bmp=null; String imgName=Tool.MD5(url); //从缓存中获取位图 bmp=getCacheBitmap(imgName); if(bmp==null) { //Log.v(TAG, "getCacheBitmap(imgName) is return null"); bmp=getLocalBitmap(imgName); if(bmp==null) { //Log.v(TAG, "getLocalBitmap(imgName) is return null"); bmp=getWebBitmap(url,imgName); } } if(bmp!=null) { //更新文件最近被使用时间 updateFileTime(imgName); } return bmp; } } /** * 从缓存中获取图片 */ private Bitmap getCacheBitmap(String imgName) { // 先从mHardBitmapCache缓存中获取 synchronized (bitmapCache) { final Bitmap bitmap =bitmapCache.get(imgName); if (bitmap != null) { //如果找到的话,把元素移到linkedhashmap的最前面,从而保证在LRU算法中是最后被删除 // bitmapCache.remove(imgName); //bitmapCache.put(imgName,bitmap); return bitmap; } } //如果mHardBitmapCache中找不到,到mSoftBitmapCache中找 SoftReference bitmapReference = softBitmapCache.get(imgName); if (bitmapReference != null) { final Bitmap bitmap =bitmapReference.get(); if (bitmap != null) { bitmapCache.put(imgName, bitmap); return bitmap; } else { softBitmapCache.remove(imgName); } } return null; } /* * 本地获取图片信息 * @param url 图片路径 * @return bitmap对象 */ private Bitmap getLocalBitmap(String imgName) { String path =imgDir+ File.separator + imgName; File f = new File(path); // 当图片文件在sd卡中存在时,从sd卡中获取位图 if (f.exists()&&f.isFile()) { Bitmap bitmap =decodeFile(imgDir+ File.separator + imgName); if(bitmap!=null) { bitmapCache.put(imgName,bitmap); } return bitmap; } return null; } /* * 抓取远程图片 * @param imgUrl 图片地址 * @return */ private Bitmap getWebBitmap(String imgUrl,String imgName) { Bitmap bitmap=Tool.getWebBitmap(imgUrl); if(bitmap!=null) { //将位图放入缓存 bitmapCache.put(imgName,bitmap); try { //如果sd卡中图片数量过多,则用LRU算法清除一些图片 removeCache(); //将文件保存在sd卡 saveInSDCard(bitmap,imgName); } catch(Exception e) { e.printStackTrace(); } } return bitmap; } private Bitmap decodeFile(String path) { BitmapFactory.Options bfOptions=new BitmapFactory.Options(); bfOptions.inDither=false; bfOptions.inPurgeable=true; bfOptions.inInputShareable=true; bfOptions.inTempStorage=new byte[ConfigBLL.ImageMaxWidth * ConfigBLL.ImageMaxHeight]; File file=new File(path); FileInputStream fs=null; try { fs = new FileInputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } Bitmap bm=null; try { if(fs!=null) bm=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions); } catch (IOException e) { e.printStackTrace(); } finally { if(fs!=null) { try { fs.close(); } catch (IOException e) { e.printStackTrace(); } } } return bm; } /* * 把位图保持在sdcard * @param bmp 要保存的位图 * @param imgName图片的名字 */ private void saveInSDCard(Bitmap bmp,String imgName) { File f=new File(imgDir); if(!f.exists()) { f.mkdirs(); } File imgFile=new File(f,imgName); try { imgFile.createNewFile(); FileOutputStream fos=new FileOutputStream(imgFile); bmp.compress(CompressFormat.JPEG, 70, fos); fos.flush(); fos.close(); } catch(Exception e) { e.printStackTrace(); } } /** * 修改文件的最后修改时间 * @param fileName */ private void updateFileTime(String imgName) { File file = new File(imgDir,imgName); long newModifiedTime =System.currentTimeMillis(); file.setLastModified(newModifiedTime); } /** *计算存储目录下的文件大小,如果图片的容量超标或者sd容量不足minSDCacheSiz则 *删除40%最近没有被使用的文件 */ private void removeCache() { File f = new File(imgDir); File[] files = f.listFiles(); //这样使得每下载20张图片才会检查是否要清除多余的图片 if (files == null) { return; } /*int dirSize = 0; for (int i = 0; i < files.length;i++) { dirSize += files[i].length(); } */ if (files.length> maxImgCount||minSDCacheSize > getSdcardFreeSpace()) { int removeFactor = (int) ((0.4 *files.length) + 1); Arrays.sort(files, comparatorFile); for (int i = 0; i comparatorFile=new Comparator() { public int compare(File arg0, File arg1) { if (arg0.lastModified() >arg1.lastModified()) { return 1; } else if (arg0.lastModified() ==arg1.lastModified()) { return 0; } else { return -1; } } }; /* * 图片的缓存容器,当map的size大于20时,把最近不常用的key放到softBitmapCache中,从而保证bitmapCache的效率 */ private final HashMap bitmapCache = new LinkedHashMap(cacheSize/ 2, 0.75f, true) { /** * */ private static final long serialVersionUID = 1L; @Override protected boolean removeEldestEntry(LinkedHashMap.Entry eldest) { if (size() >cacheSize) { //当map的size大于20时,把最近不常用的key放到softBitmapCache中,从而保证bitmapCache的效率 softBitmapCache.put(eldest.getKey(), new SoftReference(eldest.getValue())); return true; } else { return false; } } }; /** *当bitmapCache的key大于20的时候,会根据LRU算法把最近没有被使用的key放入到这个缓存中。 *Bitmap使用了SoftReference,当内存空间不足时,此cache中的bitmap会被垃圾回收掉 */ private final ConcurrentHashMap> softBitmapCache = new ConcurrentHashMap>(cacheSize / 2); }