Android性能优化之实现双缓存的图片异步加载工具(LruCache+SoftReference)

来源:互联网 发布:网络产业未来发展趋势 编辑:程序博客网 时间:2024/05/22 09:05

Android性能优化之实现双缓存的图片异步加载工具(LruCache+SoftReference) - 拿来即用

标签: 性能优化android开发缓存安卓
 969人阅读 评论(0) 收藏 举报
 分类:

之前在郭大神的博客看到使用LruCache算法实现图片缓存的.这里仿效他的思路,自己也写了一个. 并加入ConcurrentHashMap<String, SoftReference<Bitmap>>去实现二级缓存,因为ConcurrentHashMap是多个锁的线程安全,支持高并发.很适合这种频繁访问读取内存的操作.


下面整个思路是,使用了系统提供的LruCache类做一级缓存, 大小为运行内存的1/8,当LruCache容量要满的时候,会自动将系统移除的图片放到二级缓存中,但为了避免OOM的问题,这里将SoftReference软引用加入来,当系统快要OOM的时候会自动清除里面的图片内存,当然内存充足时就会继续保存这些二级缓存的图片.强调一点,不要用SoftReference去做一级缓存,现在的Java中垃圾回收加强了对SoftReference软引用的回收机制,它只适合临时的保存一些数据缓存,并不适合长期的(相对临时而言,并不是真正的长期).


直接上代码,拿来即用哦:

[java] view plain copy
  1. /** 
  2.  * Created on 3/11/2015 
  3.  * <br>图片异步加载工具(支持本地图片加载,网络图片URL和项目内图片资源加载)  
  4.  * <br>支持双缓存: LruCache和SoftReference 
  5.  * @author Mr.Et 
  6.  * 
  7.  */  
  8. public class ImageLoadManager {  
  9.     /** 图片源类型: 文件,网络,资源ID **/  
  10.     public enum IMAGE_LOAD_TYPE  
  11.     {  
  12.         FILE_PATH,FILE_URL,FILE_RESOURCE_ID  
  13.     }  
  14.       
  15.     private String TAG = "ImageLoadManager...";  
  16.       
  17.     private Context context;  
  18.       
  19.     private Set<ImageLoadTask> taskCollection;  
  20.       
  21.     /** 最大内存 **/  
  22.     final static int maxCacheSize = (int)(Runtime.getRuntime().maxMemory() / 8);  
  23.       
  24.     /** 建立线程安全,支持高并发的容器 **/  
  25.     private static ConcurrentHashMap<String, SoftReference<Bitmap>> currentHashmap  
  26.         = new ConcurrentHashMap<String, SoftReference<Bitmap>>();  
  27.       
  28.       
  29.       
  30.       
  31.       
  32.       
  33.       
  34.     public ImageLoadManager(Context context)  
  35.     {  
  36.         super();  
  37.         this.context = context;  
  38.         taskCollection = new HashSet<ImageLoadManager.ImageLoadTask>();  
  39.     }  
  40.       
  41.     private static LruCache<String, Bitmap> BitmapMemoryCache = new LruCache<String, Bitmap>(maxCacheSize)  
  42.     {  
  43.         @Override  
  44.         protected int sizeOf(String key, Bitmap value)  
  45.         {  
  46.             if(value != null)  
  47.             {  
  48.                 return value.getByteCount();  
  49.                 //return value.getRowBytes() * value.getHeight();   //旧版本的方法  
  50.             }  
  51.             else  
  52.             {  
  53.                 return 0;  
  54.             }  
  55.         }  
  56.           
  57.         //这个方法当LruCache的内存容量满的时候会调用,将oldValue的元素移除出来腾出空间给新的元素加入  
  58.         @Override  
  59.         protected void entryRemoved(boolean evicted, String key,Bitmap oldValue, Bitmap newValue)  
  60.         {  
  61.             if(oldValue != null)  
  62.             {  
  63.                 // 当硬引用缓存容量已满时,会使用LRU算法将最近没有被使用的图片转入软引用缓存      
  64.                 currentHashmap.put(key, new SoftReference<Bitmap>(oldValue));  
  65.             }  
  66.         }  
  67.           
  68.     };  
  69.       
  70.     /** 
  71.      * 针对提供图片资源ID来显示图片的方法 
  72.      * @param loadType  图片加载类型 
  73.      * @param imageResourceID   图片资源id 
  74.      * @param imageView 显示图片的ImageView 
  75.      */  
  76.     public void setImageView(IMAGE_LOAD_TYPE loadType, int imageResourceID, ImageView imageView)  
  77.     {  
  78.         if(loadType == IMAGE_LOAD_TYPE.FILE_RESOURCE_ID)  
  79.         {  
  80. //          if(ifResourceIdExist(imageResourceID))  
  81. //          {  
  82. //              imageView.setImageResource(imageResourceID);  
  83. //                
  84. //          }else{  //映射无法获取该图片,则显示默认图片  
  85. //              imageView.setImageResource(R.drawable.pic_default);  
  86. //          }  
  87.             try   
  88.             {  
  89.                 imageView.setImageResource(imageResourceID);  
  90.                 return;  
  91.             } catch (Exception e) {  
  92.                 Log.e(TAG, "Can find the imageID of "+imageResourceID);  
  93.                 e.printStackTrace();  
  94.             }  
  95.             //默认图片  
  96.             imageView.setImageResource(R.drawable.pic_default);  
  97.         }  
  98.     }  
  99.       
  100.     /** 
  101.      * 针对提供图片文件链接或下载链接来显示图片的方法 
  102.      * @param loadType  图片加载类型 
  103.      * @param imageFilePath 图片文件的本地文件地址或网络URL的下载链接 
  104.      * @param imageView 显示图片的ImageView 
  105.      */  
  106.     public void setImageView(IMAGE_LOAD_TYPE loadType, String imageFilePath, ImageView imageView)  
  107.     {  
  108.         if(imageFilePath == null || imageFilePath.trim().equals(""))  
  109.         {  
  110.             imageView.setImageResource(R.drawable.pic_default);  
  111.               
  112.         }else{  
  113.             Bitmap bitmap = getBitmapFromMemoryCache(imageFilePath);  
  114.             if(bitmap != null)  
  115.             {  
  116.                 imageView.setImageBitmap(bitmap);  
  117.             }  
  118.             else  
  119.             {  
  120.                 imageView.setImageResource(R.drawable.pic_default);  
  121.                 ImageLoadTask task = new ImageLoadTask(loadType, imageView);  
  122.                 taskCollection.add(task);  
  123.                 task.execute(imageFilePath);  
  124.             }  
  125.         }  
  126.     }  
  127.       
  128.     /** 
  129.      * 从LruCache中获取一张图片,如果不存在就返回null 
  130.      * @param key  键值可以是图片文件的filePath,可以是图片URL地址 
  131.      * @return Bitmap对象,或者null 
  132.      */  
  133.     public Bitmap getBitmapFromMemoryCache(String key)  
  134.     {     
  135.         try   
  136.         {  
  137.             if(BitmapMemoryCache.get(key) == null)  
  138.             {  
  139.                 if(currentHashmap.get(key) != null)  
  140.                 {  
  141.                     return currentHashmap.get(key).get();  
  142.                 }  
  143.             }  
  144.             return BitmapMemoryCache.get(key);  
  145.               
  146.         } catch (Exception e) {  
  147.             e.printStackTrace();  
  148.         }  
  149.         return BitmapMemoryCache.get(key);  
  150.     }  
  151.       
  152.     /** 
  153.      * 将图片放入缓存 
  154.      * @param key 
  155.      * @param bitmap 
  156.      */  
  157.     private void addBitmapToCache(String key, Bitmap bitmap)  
  158.     {  
  159.         BitmapMemoryCache.put(key, bitmap);  
  160.     }  
  161.       
  162.       
  163.     /** 
  164.      * 图片异步加载 
  165.      * @author Mr.Et 
  166.      * 
  167.      */  
  168.     private class ImageLoadTask extends AsyncTask<String, Void, Bitmap>  
  169.     {  
  170.         private String imagePath;  
  171.         private ImageView imageView;  
  172.         private IMAGE_LOAD_TYPE loadType;  
  173.           
  174.         public ImageLoadTask(IMAGE_LOAD_TYPE loadType , ImageView imageView)  
  175.         {  
  176.             this.loadType = loadType;  
  177.             this.imageView = imageView;  
  178.         }  
  179.           
  180.         @Override  
  181.         protected Bitmap doInBackground(String...params)  
  182.         {  
  183.             imagePath = params[0];  
  184.             try   
  185.             {  
  186.                 if(loadType == IMAGE_LOAD_TYPE.FILE_PATH)  
  187.                 {  
  188.                     if(new File(imagePath).exists())  
  189.                     {   //从本地FILE读取图片  
  190.                         BitmapFactory.Options opts = new BitmapFactory.Options();  
  191.                         opts.inSampleSize = 2;  
  192.                         Bitmap bitmap = BitmapFactory.decodeFile(imagePath, opts);  
  193.                         //将获取的新图片放入缓存  
  194.                         addBitmapToCache(imagePath, bitmap);  
  195.                         return bitmap;  
  196.                     }  
  197.                     return null;  
  198.                 }  
  199.                 else if(loadType == IMAGE_LOAD_TYPE.FILE_URL)  
  200.                 {   //从网络下载图片  
  201.                     byte[] datas = getBytesOfBitMap(imagePath);  
  202.                     if(datas != null)  
  203.                     {  
  204. //                      BitmapFactory.Options opts = new BitmapFactory.Options();  
  205. //                      opts.inSampleSize = 2;  
  206. //                      Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length, opts);  
  207.                         Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length);  
  208.                         addBitmapToCache(imagePath, bitmap);  
  209.                         return bitmap;  
  210.                     }  
  211.                     return null;  
  212.                 }  
  213.                   
  214.             } catch (Exception e) {  
  215.                 e.printStackTrace();  
  216.                 FileUtils.saveExceptionLog(e);  
  217.                 //可自定义其他操作  
  218.             }  
  219.             return null;  
  220.         }  
  221.           
  222.         @Override  
  223.         protected void onPostExecute(Bitmap bitmap)  
  224.         {  
  225.             try   
  226.             {  
  227.                 if(imageView != null)  
  228.                 {  
  229.                     if(bitmap != null)  
  230.                     {  
  231.                         imageView.setImageBitmap(bitmap);  
  232.                     }  
  233.                     else  
  234.                     {  
  235.                         Log.e(TAG, "The bitmap result is null...");  
  236.                     }  
  237.                 }  
  238.                 else  
  239.                 {  
  240.                     Log.e(TAG, "The imageView is null...");  
  241.                     //获取图片失败时显示默认图片  
  242.                     imageView.setImageResource(R.drawable.pic_default);  
  243.                 }  
  244.                   
  245.             } catch (Exception e) {  
  246.                 e.printStackTrace();  
  247.                 FileUtils.saveExceptionLog(e);  
  248.             }  
  249.         }  
  250.           
  251.           
  252.     }  
  253.       
  254.       
  255.     /** 
  256.      * InputStream转byte[] 
  257.      * @param inStream 
  258.      * @return 
  259.      * @throws Exception 
  260.      */  
  261.     private byte[] readStream(InputStream inStream) throws Exception{    
  262.         ByteArrayOutputStream outStream = new ByteArrayOutputStream();    
  263.         byte[] buffer = new byte[2048];    
  264.         int len = 0;    
  265.         while( (len=inStream.read(buffer)) != -1){    
  266.             outStream.write(buffer, 0, len);    
  267.         }    
  268.         outStream.close();    
  269.         inStream.close();    
  270.         return outStream.toByteArray();    
  271.     }  
  272.       
  273.     /** 
  274.      * 获取下载图片并转为byte[] 
  275.      * @param urlStr 
  276.      * @return 
  277.      */  
  278.     private byte[] getBytesOfBitMap(String imgUrl){  
  279.         try {  
  280.             URL url = new URL(imgUrl);  
  281.             HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  282.             conn.setConnectTimeout(10 * 1000);  //10s  
  283.             conn.setReadTimeout(20 * 1000);  
  284.             conn.setRequestMethod("GET");    
  285.             conn.connect();  
  286.             InputStream in = conn.getInputStream();  
  287.             return readStream(in);  
  288.         } catch (IOException e) {  
  289.             e.printStackTrace();  
  290.         } catch (Exception e) {  
  291.             e.printStackTrace();  
  292.         }  
  293.         return null;  
  294.     }  
  295.       
  296.     /** 
  297.      * 该资源ID是否有效 
  298.      * @param resourceId 资源ID 
  299.      * @return 
  300.      */  
  301.     private boolean ifResourceIdExist(int resourceId)  
  302.     {  
  303.         try   
  304.         {  
  305.             Field field = R.drawable.class.getField(String.valueOf(resourceId));  
  306.             Integer.parseInt(field.get(null).toString());  
  307.             return true;  
  308.               
  309.         } catch (Exception e) {  
  310.             e.printStackTrace();  
  311.         }   
  312.         return false;  
  313.     }  
  314.       
  315.     /** 
  316.      * 取消所有任务 
  317.      */  
  318.     public void cancelAllTask()  
  319.     {  
  320.         if(taskCollection != null){  
  321.             for(ImageLoadTask task : taskCollection)  
  322.             {  
  323.                 task.cancel(false);  
  324.             }  
  325.         }  
  326.     }  
  327.       
  328.       
  329. }  

In addition, 如果需要更加完美的体验,还可以加入第三级的缓存机制, 比如将图片缓存到本地的磁盘存储空间中.但是又不想这些缓存在本地的图片被其他应用扫描到或者被用户看到怎么办? 这里有几个思路, 比如将图片用加密算法转为字符串存储,或者将图片转为自定义格式的未知文件去放在隐蔽的地方(很多应用都采取了这种方式). 这个不妨自己去尝试实现哦~
阅读全文
0 0