android多级图片缓存

来源:互联网 发布:免费手机相册制作软件 编辑:程序博客网 时间:2024/04/28 17:36

当要加载网络图片时使用缓存能提高性能又能减少服务器的压力。为了兼顾速度与缓存图片的数量应使用多级缓存

1传入url从LinkedHashMap获取图片,LinkedHashMap可以采用LRU算法按顺序插入元素,这样LinkedHashMap可以只保存最近使用的N个元素。

2如果hashmap没有此图片则从 ConcurrentHashMap<String, SoftReference<Bitmap>>获取图片。这里使用了SoftReference(软引用) 如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。软引用与LinkedHashMap配合使用,当LinkedHashMap插入元素时,如果LinkedHashMap的元素数目大于某个值,则把这个元素插入 ConcurrentHashMap<String, SoftReference<Bitmap>>中,而不是插入LinkedHashMap中。这样保证了速度又使得缓存图片的数量尽量的大。

3如果 ConcurrentHashMap<String, SoftReference<Bitmap>>没有找到这个图片,则从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<Bitmap> 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 <removeFactor; i++) 
           {
            files[i].delete();              
           } 
    
       } 
    
   } 
   
   /*
    * 获取sdcard上的剩余空间 ,md为单位
    * @return 
    */ 
   private int getSdcardFreeSpace() 
   { 
      StatFs stat = new StatFs(Environment.getExternalStorageDirectory() .getPath()); 
      return (int)(((double)stat.getAvailableBlocks() * (double) stat.getBlockSize()) / mb); 
   } 
   
   /** 
    *  根据文件的最后修改时间进行排序  
    */ 
   Comparator<File> comparatorFile=new Comparator<File>()
   { 
       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<String, Bitmap> bitmapCache = new LinkedHashMap<String, Bitmap>(cacheSize/ 2, 0.75f, true) 
   { 
       /**

*/
private static final long serialVersionUID = 1L;


@Override 
       protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) 
       { 
           if (size() >cacheSize) 
           { 
              //当map的size大于20时,把最近不常用的key放到softBitmapCache中,从而保证bitmapCache的效率 
              softBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue())); 
              return true; 
           }
           else
           {
               return false; 
           }
       } 
   }; 
   
   /** 
    *当bitmapCache的key大于20的时候,会根据LRU算法把最近没有被使用的key放入到这个缓存中。 
    *Bitmap使用了SoftReference,当内存空间不足时,此cache中的bitmap会被垃圾回收掉 
    */ 
   private final  ConcurrentHashMap<String, SoftReference<Bitmap>> softBitmapCache =
    new ConcurrentHashMap<String,SoftReference<Bitmap>>(cacheSize / 2); 


}
原创粉丝点击