android中图片的三级cache策略(内存、文件、网络)

  1.  一  
  2.   
  3.   
  4.   
  5.   
  6. 1. 简介  
  7.    
  8. 现在android应用中不可避免的要使用图片,有些图片是可以变化的,需要每次启动时从网络拉取,这种场景在有广告位的应用以及纯图片应用(比如百度美拍)中比较多。  
  9.    
  10. 现在有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响。当然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略。总之,图片缓存是很重要而且是必须的。  
  11.    
  12.    
  13.    
  14. 2.图片缓存的原理  
  15.    
  16. 实现图片缓存也不难,需要有相应的cache策略。这里我采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且也把它划到缓存的层次结构中。当根据url向网络拉取图片的时候,先从内存中找,如果内存中没有,再从缓存文件中查找,如果缓存文件中也没有,再从网络上通过http请求拉取图片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是bitmap。所以,按照这个逻辑,只要一个url被下载过,其图片就被缓存起来了。  
  17.    
  18.   
  19.   
  20.   
  21. 关于Java中对象的软引用(SoftReference),如果一个对象具有软引用,内存空间足够,垃 圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高 速缓存。使用软引用能防止内存泄露,增强程序的健壮性。    
  22.    
  23.   
  24.   
  25.   
  26. 从代码上来说,采用一个ImageManager来负责图片的管理和缓存,函数接口为public void loadBitmap(String url, Handler handler) ;其中url为要下载的图片地址,handler为图片下载成功后的回调,在handler中处理message,而message中包含了图片的信息以及bitmap对象。ImageManager中使用的ImageMemoryCache(内存缓存)、ImageFileCache(文件缓存)以及LruCache(最近最久未使用缓存)会在后续文章中介绍。  
  27.    
  28.   
  29.   
  30.   
  31. 3.代码ImageManager.java  
  32.    
  33.    
  34.  /* 
  35.  * 图片管理 
  36.  * 异步获取图片,直接调用loadImage()函数,该函数自己判断是从缓存还是网络加载 
  37.  * 同步获取图片,直接调用getBitmap()函数,该函数自己判断是从缓存还是网络加载 
  38.  * 仅从本地获取图片,调用getBitmapFromNative() 
  39.  * 仅从网络加载图片,调用getBitmapFromHttp() 
  40.  *  
  41.  */  
  42.   
  43. public class ImageManager implements IManager  
  44. {  
  45.     private final static String TAG = "ImageManager";  
  46.       
  47.     private ImageMemoryCache imageMemoryCache; //内存缓存  
  48.       
  49.     private ImageFileCache   imageFileCache; //文件缓存  
  50.       
  51.     //正在下载的image列表  
  52.     public static HashMap<String, Handler> ongoingTaskMap = new HashMap<String, Handler>();  
  53.       
  54.     //等待下载的image列表  
  55.     public static HashMap<String, Handler> waitingTaskMap = new HashMap<String, Handler>();  
  56.       
  57.     //同时下载图片的线程个数  
  58.     final static int MAX_DOWNLOAD_IMAGE_THREAD = 4;  
  59.       
  60.     private final Handler downloadStatusHandler = new Handler(){  
  61.         public void handleMessage(Message msg)  
  62.         {  
  63.             startDownloadNext();  
  64.         }  
  65.     };  
  66.       
  67.     public ImageManager()  
  68.     {  
  69.         imageMemoryCache = new ImageMemoryCache();  
  70.         imageFileCache = new ImageFileCache();  
  71.     }  
  72.       
  73.     /** 
  74.      * 获取图片,多线程的入口 
  75.      */  
  76.     public void loadBitmap(String url, Handler handler)   
  77.     {  
  78.         //先从内存缓存中获取,取到直接加载  
  79.         Bitmap bitmap = getBitmapFromNative(url);  
  80.           
  81.         if (bitmap != null)  
  82.         {  
  83.             Logger.d(TAG, "loadBitmap:loaded from native");  
  84.             Message msg = Message.obtain();  
  85.             Bundle bundle = new Bundle();  
  86.             bundle.putString("url", url);  
  87.             msg.obj = bitmap;  
  88.             msg.setData(bundle);  
  89.             handler.sendMessage(msg);  
  90.         }   
  91.         else  
  92.         {  
  93.             Logger.d(TAG, "loadBitmap:will load by network");  
  94.             downloadBmpOnNewThread(url, handler);  
  95.         }  
  96.     }  
  97.       
  98.     /** 
  99.      * 新起线程下载图片 
  100.      */  
  101.     private void downloadBmpOnNewThread(final String url, final Handler handler)  
  102.     {  
  103.         Logger.d(TAG, "ongoingTaskMap'size=" + ongoingTaskMap.size());  
  104.           
  105.         if (ongoingTaskMap.size() >= MAX_DOWNLOAD_IMAGE_THREAD)   
  106.         {  
  107.             synchronized (waitingTaskMap)   
  108.             {  
  109.                 waitingTaskMap.put(url, handler);  
  110.             }  
  111.         }   
  112.         else   
  113.         {  
  114.             synchronized (ongoingTaskMap)   
  115.             {  
  116.                 ongoingTaskMap.put(url, handler);  
  117.             }  
  118.   
  119.             new Thread()   
  120.             {  
  121.                 public void run()   
  122.                 {  
  123.                     Bitmap bmp = getBitmapFromHttp(url);  
  124.   
  125.                     // 不论下载是否成功,都从下载队列中移除,再由业务逻辑判断是否重新下载  
  126.                     // 下载图片使用了httpClientRequest,本身已经带了重连机制  
  127.                     synchronized (ongoingTaskMap)   
  128.                     {  
  129.                         ongoingTaskMap.remove(url);  
  130.                     }  
  131.                       
  132.                     if(downloadStatusHandler != null)  
  133.                     {  
  134.                         downloadStatusHandler.sendEmptyMessage(0);  
  135.                       
  136.                     }  
  137.   
  138.                     Message msg = Message.obtain();  
  139.                     msg.obj = bmp;  
  140.                     Bundle bundle = new Bundle();  
  141.                     bundle.putString("url", url);  
  142.                     msg.setData(bundle);  
  143.                       
  144.                     if(handler != null)  
  145.                     {  
  146.                         handler.sendMessage(msg);  
  147.                     }  
  148.   
  149.                 }  
  150.             }.start();  
  151.         }  
  152.     }  
  153.   
  154.       
  155.     /** 
  156.      * 依次从内存,缓存文件,网络上加载单个bitmap,不考虑线程的问题 
  157.      */  
  158.     public Bitmap getBitmap(String url)  
  159.     {  
  160.         // 从内存缓存中获取图片  
  161.         Bitmap bitmap = imageMemoryCache.getBitmapFromMemory(url);  
  162.         if (bitmap == null)   
  163.         {  
  164.             // 文件缓存中获取  
  165.             bitmap = imageFileCache.getImageFromFile(url);  
  166.             if (bitmap != null)   
  167.             {                 
  168.                 // 添加到内存缓存  
  169.                 imageMemoryCache.addBitmapToMemory(url, bitmap);  
  170.             }   
  171.             else   
  172.             {  
  173.                 // 从网络获取  
  174.                 bitmap = getBitmapFromHttp(url);  
  175.             }  
  176.         }  
  177.         return bitmap;  
  178.     }  
  179.       
  180.     /** 
  181.      * 从内存或者缓存文件中获取bitmap 
  182.      */  
  183.     public Bitmap getBitmapFromNative(String url)  
  184.     {  
  185.         Bitmap bitmap = null;  
  186.         bitmap = imageMemoryCache.getBitmapFromMemory(url);  
  187.           
  188.         if(bitmap == null)  
  189.         {  
  190.             bitmap = imageFileCache.getImageFromFile(url);  
  191.             if(bitmap != null)  
  192.             {  
  193.                 // 添加到内存缓存  
  194.                 imageMemoryCache.addBitmapToMemory(url, bitmap);  
  195.             }  
  196.         }  
  197.         return bitmap;  
  198.     }  
  199.       
  200.     /** 
  201.      * 通过网络下载图片,与线程无关 
  202.      */  
  203.     public Bitmap getBitmapFromHttp(String url)  
  204.     {  
  205.         Bitmap bmp = null;  
  206.           
  207.         try  
  208.         {  
  209.             byte[] tmpPicByte = getImageBytes(url);  
  210.       
  211.             if (tmpPicByte != null)   
  212.             {  
  213.                 bmp = BitmapFactory.decodeByteArray(tmpPicByte, 0,  
  214.                         tmpPicByte.length);  
  215.             }  
  216.             tmpPicByte = null;  
  217.         }  
  218.         catch(Exception e)  
  219.         {  
  220.             e.printStackTrace();  
  221.         }  
  222.           
  223.         if(bmp != null)  
  224.         {  
  225.             // 添加到文件缓存  
  226.             imageFileCache.saveBitmapToFile(bmp, url);  
  227.             // 添加到内存缓存  
  228.             imageMemoryCache.addBitmapToMemory(url, bmp);  
  229.         }  
  230.   
  231.         return bmp;  
  232.     }  
  233.       
  234.     /** 
  235.      * 下载链接的图片资源 
  236.      *  
  237.      * @param url 
  238.      *             
  239.      * @return 图片 
  240.      */  
  241.     public byte[] getImageBytes(String url)   
  242.     {  
  243.         byte[] pic = null;  
  244.         if (url != null && !"".equals(url))   
  245.         {  
  246.             Requester request = RequesterFactory.getRequester(  
  247.                     Requester.REQUEST_REMOTE, RequesterFactory.IMPL_HC);  
  248.             // 执行请求  
  249.             MyResponse myResponse = null;  
  250.             MyRequest mMyRequest;  
  251.             mMyRequest = new MyRequest();  
  252.             mMyRequest.setUrl(url);  
  253.             mMyRequest.addHeader(HttpHeader.REQ.ACCEPT_ENCODING, "identity");  
  254.             InputStream is = null;  
  255.             ByteArrayOutputStream baos = null;  
  256.             try {  
  257.                 myResponse = request.execute(mMyRequest);  
  258.                 is = myResponse.getInputStream().getImpl();  
  259.                 baos = new ByteArrayOutputStream();  
  260.                 byte[] b = new byte[512];  
  261.                 int len = 0;  
  262.                 while ((len = is.read(b)) != -1)   
  263.                 {  
  264.                     baos.write(b, 0, len);  
  265.                     baos.flush();  
  266.                 }  
  267.                 pic = baos.toByteArray();  
  268.                 Logger.d(TAG, "icon bytes.length=" + pic.length);  
  269.   
  270.             }   
  271.             catch (Exception e3)   
  272.             {  
  273.                 e3.printStackTrace();  
  274.                 try   
  275.                 {  
  276.                     Logger.e(TAG,  
  277.                             "download shortcut icon faild and responsecode="  
  278.                                     + myResponse.getStatusCode());  
  279.                 }   
  280.                 catch (Exception e4)   
  281.                 {  
  282.                     e4.printStackTrace();  
  283.                 }  
  284.             }   
  285.             finally   
  286.             {  
  287.                 try   
  288.                 {  
  289.                     if (is != null)   
  290.                     {  
  291.                         is.close();  
  292.                         is = null;  
  293.                     }  
  294.                 }   
  295.                 catch (Exception e2)   
  296.                 {  
  297.                     e2.printStackTrace();  
  298.                 }  
  299.                 try   
  300.                 {  
  301.                     if (baos != null)   
  302.                     {  
  303.                         baos.close();  
  304.                         baos = null;  
  305.                     }  
  306.                 }   
  307.                 catch (Exception e2)   
  308.                 {  
  309.                     e2.printStackTrace();  
  310.                 }  
  311.                 try   
  312.                 {  
  313.                     request.close();  
  314.                 }   
  315.                 catch (Exception e1)   
  316.                 {  
  317.                     e1.printStackTrace();  
  318.                 }  
  319.             }  
  320.         }  
  321.         return pic;  
  322.     }  
  323.       
  324.     /** 
  325.      * 取出等待队列第一个任务,开始下载 
  326.      */  
  327.     private void startDownloadNext()  
  328.     {  
  329.         synchronized(waitingTaskMap)  
  330.         {     
  331.             Logger.d(TAG, "begin start next");  
  332.             Iterator iter = waitingTaskMap.entrySet().iterator();   
  333.           
  334.             while (iter.hasNext())   
  335.             {  
  336.                   
  337.                 Map.Entry entry = (Map.Entry) iter.next();  
  338.                 Logger.d(TAG, "WaitingTaskMap isn't null,url=" + (String)entry.getKey());  
  339.                   
  340.                 if(entry != null)  
  341.                 {  
  342.                     waitingTaskMap.remove(entry.getKey());  
  343.                     downloadBmpOnNewThread((String)entry.getKey(), (Handler)entry.getValue());  
  344.                 }  
  345.                 break;  
  346.             }  
  347.         }  
  348.     }  
  349.       
  350.     public String startDownloadNext_ForUnitTest()  
  351.     {  
  352.         String urlString = null;  
  353.         synchronized(waitingTaskMap)  
  354.         {  
  355.             Logger.d(TAG, "begin start next");  
  356.             Iterator iter = waitingTaskMap.entrySet().iterator();   
  357.           
  358.             while (iter.hasNext())   
  359.             {  
  360.                 Map.Entry entry = (Map.Entry) iter.next();  
  361.                 urlString = (String)entry.getKey();  
  362.                 waitingTaskMap.remove(entry.getKey());  
  363.                 break;  
  364.             }  
  365.         }  
  366.         return urlString;  
  367.     }  
  368.       
  369.     /** 
  370.      * 图片变为圆角 
  371.      * @param bitmap:传入的bitmap 
  372.      * @param pixels:圆角的度数,值越大,圆角越大 
  373.      * @return bitmap:加入圆角的bitmap 
  374.      */  
  375.     public static Bitmap toRoundCorner(Bitmap bitmap, int pixels)   
  376.     {   
  377.         if(bitmap == null)  
  378.             return null;  
  379.           
  380.         Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);   
  381.         Canvas canvas = new Canvas(output);   
  382.    
  383.         final int color = 0xff424242;   
  384.         final Paint paint = new Paint();   
  385.         final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());   
  386.         final RectF rectF = new RectF(rect);   
  387.         final float roundPx = pixels;   
  388.    
  389.         paint.setAntiAlias(true);   
  390.         canvas.drawARGB(0, 0, 0, 0);   
  391.         paint.setColor(color);   
  392.         canvas.drawRoundRect(rectF, roundPx, roundPx, paint);   
  393.    
  394.         paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));   
  395.         canvas.drawBitmap(bitmap, rect, rect, paint);   
  396.    
  397.         return output;   
  398.     }  
  399.       
  400.     public byte managerId()   
  401.     {  
  402.         return IMAGE_ID;  
  403.     }  
  404. }   
  405.    
posted @ 2016-03-16 22:54  Sharley  阅读(156)  评论(0编辑  收藏  举报