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

1. 简介

现在android应用中不可避免的要使用图片,有些图片是可以变化的,需要每次启动时从网络拉取,这种场景在有广告位的应用以及纯图片应用(比如百度美拍)中比较多。

现在有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响。当然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略。总之,图片缓存是很重要而且是必须的。

 

2.图片缓存的原理

实现图片缓存也不难,需要有相应的cache策略。这里我采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且也把它划到缓存的层次结构中。当根据url向网络拉取图片的时候,先从内存中找,如果内存中没有,再从缓存文件中查找,如果缓存文件中也没有,再从网络上通过http请求拉取图片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是bitmap。所以,按照这个逻辑,只要一个url被下载过,其图片就被缓存起来了。


关于Java中对象的软引用(SoftReference),如果一个对象具有软引用,内存空间足够,垃 圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高 速缓存。使用软引用能防止内存泄露,增强程序的健壮性。  


从代码上来说,采用一个ImageManager来负责图片的管理和缓存,函数接口为public void loadBitmap(String url, Handler handler) ;其中url为要下载的图片地址,handler为图片下载成功后的回调,在handler中处理message,而message中包含了图片的信息以及bitmap对象。ImageManager中使用的ImageMemoryCache(内存缓存)、ImageFileCache(文件缓存)以及LruCache(最近最久未使用缓存)会在后续文章中介绍。


3.代码ImageManager.java

 

  1. /* 
  2.  * 图片管理 
  3.  * 异步获取图片,直接调用loadImage()函数,该函数自己判断是从缓存还是网络加载 
  4.  * 同步获取图片,直接调用getBitmap()函数,该函数自己判断是从缓存还是网络加载 
  5.  * 仅从本地获取图片,调用getBitmapFromNative() 
  6.  * 仅从网络加载图片,调用getBitmapFromHttp() 
  7.  *  
  8.  */  
  9.   
  10. public class ImageManager implements IManager  
  11. {  
  12.     private final static String TAG = "ImageManager";  
  13.       
  14.     private ImageMemoryCache imageMemoryCache; //内存缓存   
  15.       
  16.     private ImageFileCache   imageFileCache; //文件缓存   
  17.       
  18.     //正在下载的image列表   
  19.     public static HashMap<String, Handler> ongoingTaskMap = new HashMap<String, Handler>();  
  20.       
  21.     //等待下载的image列表   
  22.     public static HashMap<String, Handler> waitingTaskMap = new HashMap<String, Handler>();  
  23.       
  24.     //同时下载图片的线程个数   
  25.     final static int MAX_DOWNLOAD_IMAGE_THREAD = 4;  
  26.       
  27.     private final Handler downloadStatusHandler = new Handler(){  
  28.         public void handleMessage(Message msg)  
  29.         {  
  30.             startDownloadNext();  
  31.         }  
  32.     };  
  33.       
  34.     public ImageManager()  
  35.     {  
  36.         imageMemoryCache = new ImageMemoryCache();  
  37.         imageFileCache = new ImageFileCache();  
  38.     }  
  39.       
  40.     /** 
  41.      * 获取图片,多线程的入口 
  42.      */  
  43.     public void loadBitmap(String url, Handler handler)   
  44.     {  
  45.         //先从内存缓存中获取,取到直接加载   
  46.         Bitmap bitmap = getBitmapFromNative(url);  
  47.           
  48.         if (bitmap != null)  
  49.         {  
  50.             Logger.d(TAG, "loadBitmap:loaded from native");  
  51.             Message msg = Message.obtain();  
  52.             Bundle bundle = new Bundle();  
  53.             bundle.putString("url", url);  
  54.             msg.obj = bitmap;  
  55.             msg.setData(bundle);  
  56.             handler.sendMessage(msg);  
  57.         }   
  58.         else  
  59.         {  
  60.             Logger.d(TAG, "loadBitmap:will load by network");  
  61.             downloadBmpOnNewThread(url, handler);  
  62.         }  
  63.     }  
  64.       
  65.     /** 
  66.      * 新起线程下载图片 
  67.      */  
  68.     private void downloadBmpOnNewThread(final String url, final Handler handler)  
  69.     {  
  70.         Logger.d(TAG, "ongoingTaskMap'size=" + ongoingTaskMap.size());  
  71.           
  72.         if (ongoingTaskMap.size() >= MAX_DOWNLOAD_IMAGE_THREAD)   
  73.         {  
  74.             synchronized (waitingTaskMap)   
  75.             {  
  76.                 waitingTaskMap.put(url, handler);  
  77.             }  
  78.         }   
  79.         else   
  80.         {  
  81.             synchronized (ongoingTaskMap)   
  82.             {  
  83.                 ongoingTaskMap.put(url, handler);  
  84.             }  
  85.   
  86.             new Thread()   
  87.             {  
  88.                 public void run()   
  89.                 {  
  90.                     Bitmap bmp = getBitmapFromHttp(url);  
  91.   
  92.                     // 不论下载是否成功,都从下载队列中移除,再由业务逻辑判断是否重新下载   
  93.                     // 下载图片使用了httpClientRequest,本身已经带了重连机制   
  94.                     synchronized (ongoingTaskMap)   
  95.                     {  
  96.                         ongoingTaskMap.remove(url);  
  97.                     }  
  98.                       
  99.                     if(downloadStatusHandler != null)  
  100.                     {  
  101.                         downloadStatusHandler.sendEmptyMessage(0);  
  102.                       
  103.                     }  
  104.   
  105.                     Message msg = Message.obtain();  
  106.                     msg.obj = bmp;  
  107.                     Bundle bundle = new Bundle();  
  108.                     bundle.putString("url", url);  
  109.                     msg.setData(bundle);  
  110.                       
  111.                     if(handler != null)  
  112.                     {  
  113.                         handler.sendMessage(msg);  
  114.                     }  
  115.   
  116.                 }  
  117.             }.start();  
  118.         }  
  119.     }  
  120.   
  121.       
  122.     /** 
  123.      * 依次从内存,缓存文件,网络上加载单个bitmap,不考虑线程的问题 
  124.      */  
  125.     public Bitmap getBitmap(String url)  
  126.     {  
  127.         // 从内存缓存中获取图片   
  128.         Bitmap bitmap = imageMemoryCache.getBitmapFromMemory(url);  
  129.         if (bitmap == null)   
  130.         {  
  131.             // 文件缓存中获取   
  132.             bitmap = imageFileCache.getImageFromFile(url);  
  133.             if (bitmap != null)   
  134.             {                 
  135.                 // 添加到内存缓存   
  136.                 imageMemoryCache.addBitmapToMemory(url, bitmap);  
  137.             }   
  138.             else   
  139.             {  
  140.                 // 从网络获取   
  141.                 bitmap = getBitmapFromHttp(url);  
  142.             }  
  143.         }  
  144.         return bitmap;  
  145.     }  
  146.       
  147.     /** 
  148.      * 从内存或者缓存文件中获取bitmap 
  149.      */  
  150.     public Bitmap getBitmapFromNative(String url)  
  151.     {  
  152.         Bitmap bitmap = null;  
  153.         bitmap = imageMemoryCache.getBitmapFromMemory(url);  
  154.           
  155.         if(bitmap == null)  
  156.         {  
  157.             bitmap = imageFileCache.getImageFromFile(url);  
  158.             if(bitmap != null)  
  159.             {  
  160.                 // 添加到内存缓存   
  161.                 imageMemoryCache.addBitmapToMemory(url, bitmap);  
  162.             }  
  163.         }  
  164.         return bitmap;  
  165.     }  
  166.       
  167.     /** 
  168.      * 通过网络下载图片,与线程无关 
  169.      */  
  170.     public Bitmap getBitmapFromHttp(String url)  
  171.     {  
  172.         Bitmap bmp = null;  
  173.           
  174.         try  
  175.         {  
  176.             byte[] tmpPicByte = getImageBytes(url);  
  177.       
  178.             if (tmpPicByte != null)   
  179.             {  
  180.                 bmp = BitmapFactory.decodeByteArray(tmpPicByte, 0,  
  181.                         tmpPicByte.length);  
  182.             }  
  183.             tmpPicByte = null;  
  184.         }  
  185.         catch(Exception e)  
  186.         {  
  187.             e.printStackTrace();  
  188.         }  
  189.           
  190.         if(bmp != null)  
  191.         {  
  192.             // 添加到文件缓存   
  193.             imageFileCache.saveBitmapToFile(bmp, url);  
  194.             // 添加到内存缓存   
  195.             imageMemoryCache.addBitmapToMemory(url, bmp);  
  196.         }  
  197.   
  198.         return bmp;  
  199.     }  
  200.       
  201.     /** 
  202.      * 下载链接的图片资源 
  203.      *  
  204.      * @param url 
  205.      *             
  206.      * @return 图片 
  207.      */  
  208.     public byte[] getImageBytes(String url)   
  209.     {  
  210.         byte[] pic = null;  
  211.         if (url != null && !"".equals(url))   
  212.         {  
  213.             Requester request = RequesterFactory.getRequester(  
  214.                     Requester.REQUEST_REMOTE, RequesterFactory.IMPL_HC);  
  215.             // 执行请求   
  216.             MyResponse myResponse = null;  
  217.             MyRequest mMyRequest;  
  218.             mMyRequest = new MyRequest();  
  219.             mMyRequest.setUrl(url);  
  220.             mMyRequest.addHeader(HttpHeader.REQ.ACCEPT_ENCODING, "identity");  
  221.             InputStream is = null;  
  222.             ByteArrayOutputStream baos = null;  
  223.             try {  
  224.                 myResponse = request.execute(mMyRequest);  
  225.                 is = myResponse.getInputStream().getImpl();  
  226.                 baos = new ByteArrayOutputStream();  
  227.                 byte[] b = new byte[512];  
  228.                 int len = 0;  
  229.                 while ((len = is.read(b)) != -1)   
  230.                 {  
  231.                     baos.write(b, 0, len);  
  232.                     baos.flush();  
  233.                 }  
  234.                 pic = baos.toByteArray();  
  235.                 Logger.d(TAG, "icon bytes.length=" + pic.length);  
  236.   
  237.             }   
  238.             catch (Exception e3)   
  239.             {  
  240.                 e3.printStackTrace();  
  241.                 try   
  242.                 {  
  243.                     Logger.e(TAG,  
  244.                             "download shortcut icon faild and responsecode="  
  245.                                     + myResponse.getStatusCode());  
  246.                 }   
  247.                 catch (Exception e4)   
  248.                 {  
  249.                     e4.printStackTrace();  
  250.                 }  
  251.             }   
  252.             finally   
  253.             {  
  254.                 try   
  255.                 {  
  256.                     if (is != null)   
  257.                     {  
  258.                         is.close();  
  259.                         is = null;  
  260.                     }  
  261.                 }   
  262.                 catch (Exception e2)   
  263.                 {  
  264.                     e2.printStackTrace();  
  265.                 }  
  266.                 try   
  267.                 {  
  268.                     if (baos != null)   
  269.                     {  
  270.                         baos.close();  
  271.                         baos = null;  
  272.                     }  
  273.                 }   
  274.                 catch (Exception e2)   
  275.                 {  
  276.                     e2.printStackTrace();  
  277.                 }  
  278.                 try   
  279.                 {  
  280.                     request.close();  
  281.                 }   
  282.                 catch (Exception e1)   
  283.                 {  
  284.                     e1.printStackTrace();  
  285.                 }  
  286.             }  
  287.         }  
  288.         return pic;  
  289.     }  
  290.       
  291.     /** 
  292.      * 取出等待队列第一个任务,开始下载 
  293.      */  
  294.     private void startDownloadNext()  
  295.     {  
  296.         synchronized(waitingTaskMap)  
  297.         {     
  298.             Logger.d(TAG, "begin start next");  
  299.             Iterator iter = waitingTaskMap.entrySet().iterator();   
  300.           
  301.             while (iter.hasNext())   
  302.             {  
  303.                   
  304.                 Map.Entry entry = (Map.Entry) iter.next();  
  305.                 Logger.d(TAG, "WaitingTaskMap isn't null,url=" + (String)entry.getKey());  
  306.                   
  307.                 if(entry != null)  
  308.                 {  
  309.                     waitingTaskMap.remove(entry.getKey());  
  310.                     downloadBmpOnNewThread((String)entry.getKey(), (Handler)entry.getValue());  
  311.                 }  
  312.                 break;  
  313.             }  
  314.         }  
  315.     }  
  316.       
  317.     public String startDownloadNext_ForUnitTest()  
  318.     {  
  319.         String urlString = null;  
  320.         synchronized(waitingTaskMap)  
  321.         {  
  322.             Logger.d(TAG, "begin start next");  
  323.             Iterator iter = waitingTaskMap.entrySet().iterator();   
  324.           
  325.             while (iter.hasNext())   
  326.             {  
  327.                 Map.Entry entry = (Map.Entry) iter.next();  
  328.                 urlString = (String)entry.getKey();  
  329.                 waitingTaskMap.remove(entry.getKey());  
  330.                 break;  
  331.             }  
  332.         }  
  333.         return urlString;  
  334.     }  
  335.       
  336.     /** 
  337.      * 图片变为圆角 
  338.      * @param bitmap:传入的bitmap 
  339.      * @param pixels:圆角的度数,值越大,圆角越大 
  340.      * @return bitmap:加入圆角的bitmap 
  341.      */  
  342.     public static Bitmap toRoundCorner(Bitmap bitmap, int pixels)   
  343.     {   
  344.         if(bitmap == null)  
  345.             return null;  
  346.           
  347.         Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);   
  348.         Canvas canvas = new Canvas(output);   
  349.    
  350.         final int color = 0xff424242;   
  351.         final Paint paint = new Paint();   
  352.         final Rect rect = new Rect(00, bitmap.getWidth(), bitmap.getHeight());   
  353.         final RectF rectF = new RectF(rect);   
  354.         final float roundPx = pixels;   
  355.    
  356.         paint.setAntiAlias(true);   
  357.         canvas.drawARGB(0000);   
  358.         paint.setColor(color);   
  359.         canvas.drawRoundRect(rectF, roundPx, roundPx, paint);   
  360.    
  361.         paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));   
  362.         canvas.drawBitmap(bitmap, rect, rect, paint);   
  363.    
  364.         return output;   
  365.     }  
  366.       
  367.     public byte managerId()   
  368.     {  
  369.         return IMAGE_ID;  
  370.     }  
  371. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值