图片的三级缓存机制
1. 简介
现在android应用中不可避免的要使用图片,有些图片是可以变化的,需要每次启动时从网络拉取,这种场景在有广告位的应用以及纯图片应用(比如百度美拍)中比较多。
现在有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很 耗流量的应用,其用户数量级肯定要受到影响。当然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略。总之,图片缓存是很重要而且是必须的。
2.图片缓存的原理
实现图片缓存也不难,需要有相应的cache策略。这里我采用 内存(memory)-本地(local)-网络(Internet) 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且也把它划到缓存的层次结 构中。当根据url向网络拉取图片的时候,先从内存中找,如果内存中没有,再从本地缓存文件中查找,如果缓存文件中也没有,再从网络上通过http请求拉取图 片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是bitmap。所以,按照这个逻辑,只要一个 url被下载过,其图片就被缓存起来了。
3.图片缓存的比较
## 三级缓存 ##
- 内存缓存, 优先加载, 速度最快
- 本地缓存, 次优先加载, 速度快
- 网络缓存, 不优先加载, 速度慢,浪费流量
网络缓存 NetCacheUtils
1 /** 2 * 3 * 从网络下载图片 4 * @author admin 5 * 6 */ 7 public class NetCacheUtils { 8 private LocalCacheUtils mlocalcacheutils; 9 private MemoryCacheUtils mmemorycacheutils; 10 11 public NetCacheUtils(LocalCacheUtils localcacheutils, MemoryCacheUtils memorycacheutils) { 12 mlocalcacheutils=localcacheutils; 13 mmemorycacheutils=memorycacheutils; 14 } 15 16 public void getBitmapFromNet(ImageView iv_photo, String url) { 17 // TODO Auto-generated method stub 18 BitmapTask bitmaptask=new BitmapTask(); 19 bitmaptask.execute(iv_photo,url);//开启AsyncTask,参数在doInBackground获取 20 } 21 /*AsyncTask 异步任务即做一些简单的异步处理 ;是handle与线程池的封装 22 * 第一个泛型:参数类型泛型 23 * 第二个泛型:更新进度泛型 24 * 第三个泛型:onProgressUpdate的返回结果的泛型 25 * 26 */ 27 28 class BitmapTask extends AsyncTask<Object, Void, Bitmap>{ 29 30 private ImageView pic; 31 private String murl; 32 /** 33 * 后台耗时方法在此执行,子线程 34 */ 35 @Override 36 protected Bitmap doInBackground(Object... params) { 37 pic = (ImageView) params[0]; 38 murl = (String) params[1]; 39 40 pic.setTag(murl);//将图片与url绑定 41 return downloadBitmap(murl); 42 } 43 /** 44 * 更新进度,主线程 45 */ 46 @Override 47 protected void onProgressUpdate(Void... values) { 48 // TODO Auto-generated method stub 49 super.onProgressUpdate(values); 50 } 51 /** 52 * 后台耗时方法结束之后,在此执行,主线程 53 */ 54 @Override 55 protected void onPostExecute(Bitmap result) { 56 if(result!=null){ 57 58 String tag = (String) pic.getTag(); 59 if(tag.equals(murl)){ 60 pic.setImageBitmap(result); 61 } 62 63 } 64 mlocalcacheutils.setBitmapTolocal(murl, result); 65 mmemorycacheutils.setBitmapTomemory(murl, result); 66 System.out.println("从网络上加载图片啦"); 67 68 } 69 } 70 71 /** 72 * 73 * 下载图片 74 * @return 75 */ 76 private Bitmap downloadBitmap(String url){ 77 HttpURLConnection conn=null; 78 try { 79 conn=(HttpURLConnection) new URL(url) 80 .openConnection(); 81 82 conn.setConnectTimeout(5000); 83 conn.setReadTimeout(5000); 84 conn.setRequestMethod("GET"); 85 conn.connect(); 86 87 int responseCode = conn.getResponseCode();//响应码 88 89 if(responseCode==200){//表示成功连接 90 InputStream inputStream = conn.getInputStream(); 91 Bitmap bitmap = BitmapFactory.decodeStream(inputStream); 92 return bitmap; 93 } 94 95 } catch (IOException e) { 96 97 e.printStackTrace(); 98 } 99 finally{ 100 conn.disconnect(); 101 } 102 return null; 103 104 } 105 }
本地缓存 LocalCacheUtils
1 public class LocalCacheUtils { 2 private static final String CACHE_PATH=Environment.getExternalStorageDirectory() 3 .getAbsolutePath()+"/zhbj_cache_52"; 4 /** 5 * 6 * 从本地读图片 7 * @param url 8 */ 9 public Bitmap getBitmapFromlocal(String url){ 10 try { 11 String filename = MD5Encoder.encode(url); 12 13 File file=new File(CACHE_PATH, filename);//通过父文件夹与自己的文件名称来创建一个文件 14 15 if(file.exists()){ 16 Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file)); 17 return bitmap; 18 } 19 20 } catch (Exception e) { 21 // TODO Auto-generated catch block 22 e.printStackTrace(); 23 } 24 return null; 25 26 } 27 28 /** 29 * 30 * 将图片写到本地 31 * @param url 32 * @param bitmap 33 */ 34 public void setBitmapTolocal(String url,Bitmap bitmap){ 35 try { 36 String filename = MD5Encoder.encode(url); 37 File file=new File(filename); 38 File parentFile = file.getParentFile(); 39 if(!parentFile.exists()){//如果文件夾不存在,則创建 40 file.mkdirs(); 41 } 42 //将图片保存到本地 43 bitmap.compress(CompressFormat.JPEG, 100, 44 new FileOutputStream(file)); 45 46 47 } catch (Exception e) { 48 // TODO Auto-generated catch block 49 e.printStackTrace(); 50 } 51 52 } 53 54 }
内存缓存 MemoryCacheUtils
1 /** 2 * 3 * 内存缓存 4 * @author admin 5 * 6 */ 7 public class MemoryCacheUtils { 8 9 private HashMap<String, Bitmap> hashlist=new HashMap<String, Bitmap>(); 10 /** 11 * 12 * 从内存中读 13 * @param url 14 * @return 15 */ 16 public Bitmap getBitmapFrommemory(String url){ 17 Bitmap bitmap = hashlist.get(url); 18 return bitmap; 19 } 20 21 22 /** 23 * 24 * 写入内存 25 * @param url 26 * @param bitmap 27 */ 28 public void setBitmapTomemory(String url,Bitmap bitmap){ 29 hashlist.put(url, bitmap); 30 } 31 }
MyBitmapUtils 调用
1 /** 2 * 3 * 4 * 自定义图片加载工具 5 * @author admin 6 * 7 */ 8 public class MyBitMaputils { 9 NetCacheUtils netcache; 10 LocalCacheUtils localcacheutils; 11 MemoryCacheUtils memorycacheutils; 12 public MyBitMaputils(){ 13 memorycacheutils=new MemoryCacheUtils(); 14 localcacheutils=new LocalCacheUtils(); 15 netcache=new NetCacheUtils(localcacheutils,memorycacheutils); 16 17 } 18 Bitmap bitmap =null; 19 public void display(ImageView iv_photo, String url) { 20 iv_photo.setImageResource(R.drawable.news_pic_default);//默认图片,防止图片的复用 21 //内存缓存 22 bitmap= memorycacheutils.getBitmapFrommemory(url); 23 if(bitmap!=null){ 24 iv_photo.setImageBitmap(bitmap); 25 System.out.println("从内存中读取图片"); 26 return; 27 } 28 //本地缓存 29 bitmap = localcacheutils.getBitmapFromlocal(url); 30 if(bitmap!=null){ 31 iv_photo.setImageBitmap(bitmap); 32 memorycacheutils.setBitmapTomemory(url, bitmap); 33 System.out.println("从本地读取图片"); 34 return;//从本地读取就不需要从网络读取了 35 } 36 37 //网络缓存(第一次) 38 netcache.getBitmapFromNet(iv_photo,url); 39 } 40 41 42 }