圖片的三級緩存機制
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 }