zoukankan      html  css  js  c++  java
  • ListView异步加载网络图片完美版之双缓存技术


       本示例参考学习了一个国外的示例:http://code.google.com/p/android-imagedownloader/,有兴趣的同学下载研究一下。
    问题描述:在这一篇博客中将会为大家讲解如何将下载回来的图片进行缓存,为了节约流量,并且提高下一次显示图片的速度,提高用户体验,所以不能够每次调用getView的时候都去从网络下载图片,就必须用到缓存。
    缓存的重点问题:如何控制缓存的大小,如果我们一直向缓存中筛数据,而没有对缓存的大小进行控制,那么最终会导致OOM
    解决方案:设置两级缓存,第一级用LinkedHashMap<String,Bitmap>保留Bitmap的强引用,但是控制缓存的大小MAX_CAPACITY=10,当继续向该缓存中存数据的时候,将会把一级缓存中的最近最少使用的元素放入二级缓存ConcurrentHashMap<String, SoftReference<Bitmap>>,二级缓存中保留的Bitmap的软引用。
    SoftReference:它保存的对象实例,除非JVM即将OutOfMemory,否则不会被GC回收。这个特性使得它特别适合设计对象Cache。对于Cache,我们希望被缓存的对象最好始终常驻内存,但是如果JVM内存吃紧,为了不发生OutOfMemoryError导致系统崩溃,必要的时候也允许JVM回收Cache的内存,待后续合适的时机再把数据重新Load到Cache中。这样可以系统设计得更具弹性。

    Java代码 复制代码 收藏代码
    1. // 0.75是加载因子为经验值,true则表示按照最近访问量的高低排序,false则表示按照插入顺序排序   
    2. private HashMap<String, Bitmap> mFirstLevelCache = new LinkedHashMap<String, Bitmap>(   
    3.             MAX_CAPACITY / 20.75f, true) {   
    4.         private static final long serialVersionUID = 1L;   
    5.         protected boolean removeEldestEntry(Entry<String, Bitmap> eldest) {   
    6.             if (size() > MAX_CAPACITY) {// 当超过一级缓存阈值的时候,将老的值从一级缓存搬到二级缓存   
    7.                 mSecondLevelCache.put(eldest.getKey(),   
    8.                         new SoftReference<Bitmap>(eldest.getValue()));   
    9.                 return true;   
    10.             }   
    11.             return false;   
    12.         };   
    13.     };    
    // 0.75是加载因子为经验值,true则表示按照最近访问量的高低排序,false则表示按照插入顺序排序
    private HashMap<String, Bitmap> mFirstLevelCache = new LinkedHashMap<String, Bitmap>(
    			MAX_CAPACITY / 2, 0.75f, true) {
    		private static final long serialVersionUID = 1L;
    		protected boolean removeEldestEntry(Entry<String, Bitmap> eldest) {
    			if (size() > MAX_CAPACITY) {// 当超过一级缓存阈值的时候,将老的值从一级缓存搬到二级缓存
    				mSecondLevelCache.put(eldest.getKey(),
    						new SoftReference<Bitmap>(eldest.getValue()));
    				return true;
    			}
    			return false;
    		};
    	};	


    加载图片:先读缓存,缓存么有就开启异步任务从网络下载

    Java代码 复制代码 收藏代码
    1. /**  
    2.      * 加载图片,如果缓存中有就直接从缓存中拿,缓存中没有就下载  
    3.      * @param url  
    4.      * @param adapter  
    5.      * @param holder  
    6.      */  
    7. public void loadImage(String url, BaseAdapter adapter, ViewHolder holder) {   
    8.         resetPurgeTimer();   
    9.         Bitmap bitmap = getBitmapFromCache(url);// 从缓存中读取   
    10.         if (bitmap == null) {   
    11.             holder.mImageView.setImageResource(R.drawable.ic_launcher);//缓存没有设为默认图片   
    12.             ImageLoadTask imageLoadTask = new ImageLoadTask();   
    13.             imageLoadTask.execute(url, adapter, holder);//执行异步任务   
    14.         } else {   
    15.             holder.mImageView.setImageBitmap(bitmap);//设为缓存图片   
    16.         }   
    17.   
    18.     }  
    /**
    	 * 加载图片,如果缓存中有就直接从缓存中拿,缓存中没有就下载
    	 * @param url
    	 * @param adapter
    	 * @param holder
    	 */
    public void loadImage(String url, BaseAdapter adapter, ViewHolder holder) {
    		resetPurgeTimer();
    		Bitmap bitmap = getBitmapFromCache(url);// 从缓存中读取
    		if (bitmap == null) {
    			holder.mImageView.setImageResource(R.drawable.ic_launcher);//缓存没有设为默认图片
    			ImageLoadTask imageLoadTask = new ImageLoadTask();
    			imageLoadTask.execute(url, adapter, holder);//执行异步任务
    		} else {
    			holder.mImageView.setImageBitmap(bitmap);//设为缓存图片
    		}
    
    	}
    

       
    读取缓存的代码:

    Java代码 复制代码 收藏代码
    1. public Bitmap getBitmapFromCache(String url) {   
    2.         Bitmap bitmap = null;   
    3.         bitmap = getFromFirstLevelCache(url);// 从一级缓存中拿   
    4.         if (bitmap != null) {   
    5.             return bitmap;   
    6.         }   
    7.         bitmap = getFromSecondLevelCache(url);//从二级缓存中拿   
    8.         return bitmap;   
    9.     }   
    10. private Bitmap getFromFirstLevelCache(String url) {   
    11.         Bitmap bitmap = null;   
    12.         synchronized (mFirstLevelCache) {   
    13.             bitmap = mFirstLevelCache.get(url);   
    14.             if (bitmap != null) {// 将最近访问的元素放到链的头部,提高下一次访问该元素的检索速度(LRU算法)   
    15.                 mFirstLevelCache.remove(url);   
    16.                 mFirstLevelCache.put(url, bitmap);   
    17.             }   
    18.         }   
    19.         return bitmap;   
    20.     }   
    21. private Bitmap getFromSecondLevelCache(String url) {   
    22.         Bitmap bitmap = null;   
    23.         SoftReference<Bitmap> softReference = mSecondLevelCache.get(url);   
    24.         if (softReference != null) {   
    25.             bitmap = softReference.get();   
    26.             if (bitmap == null) {// 由于内存吃紧,软引用已经被gc回收了   
    27.                 mSecondLevelCache.remove(url);   
    28.             }   
    29.         }   
    30.         return bitmap;   
    31.     }  
    public Bitmap getBitmapFromCache(String url) {
    		Bitmap bitmap = null;
    		bitmap = getFromFirstLevelCache(url);// 从一级缓存中拿
    		if (bitmap != null) {
    			return bitmap;
    		}
    		bitmap = getFromSecondLevelCache(url);//从二级缓存中拿
    		return bitmap;
    	}
    private Bitmap getFromFirstLevelCache(String url) {
    		Bitmap bitmap = null;
    		synchronized (mFirstLevelCache) {
    			bitmap = mFirstLevelCache.get(url);
    			if (bitmap != null) {// 将最近访问的元素放到链的头部,提高下一次访问该元素的检索速度(LRU算法)
    				mFirstLevelCache.remove(url);
    				mFirstLevelCache.put(url, bitmap);
    			}
    		}
    		return bitmap;
    	}
    private Bitmap getFromSecondLevelCache(String url) {
    		Bitmap bitmap = null;
    		SoftReference<Bitmap> softReference = mSecondLevelCache.get(url);
    		if (softReference != null) {
    			bitmap = softReference.get();
    			if (bitmap == null) {// 由于内存吃紧,软引用已经被gc回收了
    				mSecondLevelCache.remove(url);
    			}
    		}
    		return bitmap;
    	}
    


    定期清理缓存

    Java代码 复制代码 收藏代码
    1. // 定时清理缓存   
    2.     private Runnable mClearCache = new Runnable() {   
    3.         @Override  
    4.         public void run() {   
    5.             clear();   
    6.         }   
    7.     };   
    8.     private Handler mPurgeHandler = new Handler();   
    9.   
    10.     // 重置缓存清理的timer   
    11.     private void resetPurgeTimer() {   
    12.         mPurgeHandler.removeCallbacks(mClearCache);   
    13.         mPurgeHandler.postDelayed(mClearCache, DELAY_BEFORE_PURGE);   
    14.     }   
    15.   
    16.     /**  
    17.      * 清理缓存  
    18.      */  
    19.     private void clear() {   
    20.         mFirstLevelCache.clear();   
    21.         mSecondLevelCache.clear();   
    22.     }  
    // 定时清理缓存
    	private Runnable mClearCache = new Runnable() {
    		@Override
    		public void run() {
    			clear();
    		}
    	};
    	private Handler mPurgeHandler = new Handler();
    
    	// 重置缓存清理的timer
    	private void resetPurgeTimer() {
    		mPurgeHandler.removeCallbacks(mClearCache);
    		mPurgeHandler.postDelayed(mClearCache, DELAY_BEFORE_PURGE);
    	}
    
    	/**
    	 * 清理缓存
    	 */
    	private void clear() {
    		mFirstLevelCache.clear();
    		mSecondLevelCache.clear();
    	}
    


    总结:这篇文章主要讲了图片的缓存技巧,拿来主义,学习从别人的代码中吸取精华,代码我也上传了,并且附有详细的注释,这里的缓存都是在内存当中,适合短期有效的缓存,如果是长期有效的图片,我们可以采用文件存储的方式,再设一级文件缓存,有兴趣的同学可以研究一下。

    • 大小: 95.4 KB
    路漫漫其修远兮 吾将上下而求索
  • 相关阅读:
    pipelinewise 学习二 创建一个简单的pipeline
    pipelinewise 学习一 docker方式安装
    Supercharging your ETL with Airflow and Singer
    ubuntu中使用 alien安装rpm包
    PipelineWise illustrates the power of Singer
    pipelinewise 基于singer 指南的的数据pipeline 工具
    关于singer elt 的几篇很不错的文章
    npkill 一个方便的npm 包清理工具
    kuma docker-compose 环境试用
    kuma 学习四 策略
  • 原文地址:https://www.cnblogs.com/hudabing/p/3210655.html
Copyright © 2011-2022 走看看