Android圖片三級緩存的原理及其實現

分類:IT技術 時間:2017-09-27

為什麽要使用三級緩存

    如今的 android App 經常會需要網絡交互,通過網絡獲取圖片是再正常不過的事了 假如每次啟動的時候都從網絡拉取圖片的話,勢必會消耗很多流量。在當前的狀況下,對於非WIFI用戶來說,流量還是很貴的,一個很耗流量的應用,其用戶數量級肯定要受到影響 特別是,當我們想要重復瀏覽一些圖片時,如果每一次瀏覽都需要通過網絡獲取,流量的浪費可想而知 所以提出三級緩存策略,通過網絡、本地、內存三級緩存圖片,來減少不必要的網絡交互,避免浪費流量

什麽是三級緩存

    網絡緩存, 不優先加載, 速度慢,浪費流量 本地緩存, 次優先加載, 速度快 內存緩存, 優先加載, 速度最快

三級緩存原理

    首次加載 Android App 時,肯定要通過網絡交互來獲取圖片,之後我們可以將圖片保存至本地SD卡和內存中 之後運行 App 時,優先訪問內存中的圖片緩存,若內存中沒有,則加載本地SD卡中的圖片 總之,只在初次訪問新內容時,才通過網絡獲取圖片資源

具體實現及代碼

1. 自定義的圖片緩存工具類(MyBitmapUtils)

    通過new MyBitmapUtils().display(ImageView ivPic, String url) 提供給外部方法進行圖片緩存的接口 參數含義:ivPic 用於顯示圖片的ImageView,url 獲取圖片的網絡地址
 /**
  * 自定義的BitmapUtils,實現三級緩存
  */
 public class MyBitmapUtils {

  private NetCacheUtils mNetCacheUtils;
  private LocalCacheUtils mLocalCacheUtils;
  private MemoryCacheUtils mMemoryCacheUtils;

  public MyBitmapUtils(){
   mMemoryCacheUtils=new MemoryCacheUtils();
   mLocalCacheUtils=new LocalCacheUtils();
   mNetCacheUtils=new NetCacheUtils(mLocalCacheUtils,mMemoryCacheUtils);
  }

  public void disPlay(ImageView ivPic, String url) {
   ivPic.setImageResource(R.mipmap.pic_item_list_default);
   Bitmap bitmap;
   //內存緩存
   bitmap=mMemoryCacheUtils.getBitmapFromMemory(url);
   if (bitmap!=null){
    ivPic.setImageBitmap(bitmap);
    system.out.println("從內存獲取圖片啦.....");
    return;
   }

   //本地緩存
   bitmap = mLocalCacheUtils.getBitmapFromLocal(url);
   if(bitmap !=null){
    ivPic.setImageBitmap(bitmap);
    System.out.println("從本地獲取圖片啦.....");
    //從本地獲取圖片後,保存至內存中
    mMemoryCacheUtils.setBitmapToMemory(url,bitmap);
    return;
   }
   //網絡緩存
   mNetCacheUtils.getBitmapFromNet(ivPic,url);
  }
 }

2. 網絡緩存(NetCacheUtils)

    網絡緩存中主要用到了AsyncTask來進行異步數據的加載 簡單來說,AsyncTask可以看作是一個對handler和線程池的封裝,通常,AsyncTask主要用於數據簡單時,handler+thread主要用於數據量多且復雜時,當然這也不是必須的,仁者見仁智者見智 同時,為了避免內存溢出的問題,我們可以在獲取網絡圖片後。對其進行圖片壓縮
 /**
  * 三級緩存之網絡緩存
  */
 public class NetCacheUtils {

  private LocalCacheUtils mLocalCacheUtils;
  private MemoryCacheUtils mMemoryCacheUtils;

  public NetCacheUtils(LocalCacheUtils localCacheUtils, MemoryCacheUtils memoryCacheUtils) {
   mLocalCacheUtils = localCacheUtils;
   mMemoryCacheUtils = memoryCacheUtils;
  }

  /**
   * 從網絡下載圖片
   * @param ivPic 顯示圖片的imageview
   * @param url 下載圖片的網絡地址
   */
  public void getBitmapFromNet(ImageView ivPic, String url) {
   new BitmapTask().execute(ivPic, url);//啟動AsyncTask

  }

  /**
   * AsyncTask就是對handler和線程池的封裝
   * 第一個泛型:參數類型
   * 第二個泛型:更新進度的泛型
   * 第三個泛型:onPostExecute的返回結果
   */
  class BitmapTask extends AsyncTask<Object, Void, Bitmap> {

   private ImageView ivPic;
   private String url;

   /**
    * 後臺耗時操作,存在於子線程中
    * @param params
    * @return
    */
   @Override
   protected Bitmap doInBackground(Object[] params) {
    ivPic = (ImageView) params[0];
    url = (String) params[1];

    return downLoadBitmap(url);
   }

   /**
    * 更新進度,在主線程中
    * @param values
    */
   @Override
   protected void onProgressupdate(Void[] values) {
    super.onProgressUpdate(values);
   }

   /**
    * 耗時方法結束後執行該方法,主線程中
    * @param result
    */
   @Override
   protected void onPostExecute(Bitmap result) {
    if (result != null) {
     ivPic.setImageBitmap(result);
     System.out.println("從網絡緩存圖片啦.....");

     //從網絡獲取圖片後,保存至本地緩存
     mLocalCacheUtils.setBitmapToLocal(url, result);
     //保存至內存中
     mMemoryCacheUtils.setBitmapToMemory(url, result);

    }
   }
  }

  /**
   * 網絡下載圖片
   * @param url
   * @return
   */
  private Bitmap downLoadBitmap(String url) {
   HttpURLConnection conn = null;
   try {
    conn = (HttpURLConnection) new URL(url).openConnection();
    conn.setConnectTimeout(5000);
    conn.setReadTimeout(5000);
    conn.setRequestMethod("GET");

    int responseCode = conn.getResponseCode();
    if (responseCode == 200) {
     //圖片壓縮
     BitmapFactory.Options options = new BitmapFactory.Options();
     options.inSampleSize=2;//寬高壓縮為原來的1/2
     options.inPreferredConfig=Bitmap.Config.ARGB_4444;
     Bitmap bitmap = BitmapFactory.decodeStream(conn.getInputStream(),null,options);
     return bitmap;
    }
   } catch (IOException e) {
    e.printStackTrace();
   } finally {
    conn.disconnect();
   }

   return null;
  }
 }

3. 本地緩存(LocalCacheUtils)

    在初次通過網絡獲取圖片後,我們可以在本地SD卡中將圖片保存起來 可以使用MD5加密圖片的網絡地址,來作為圖片的名稱保存 
 /**
  * 三級緩存之本地緩存
  */
 public class LocalCacheUtils {

  private static final String CACHE_PATH= Environment.getExternalStorageDirectory().getAbsolutePath()+"/WerbNews";

  /**
   * 從本地讀取圖片
   * @param url
   */
  public Bitmap getBitmapFromLocal(String url){
   String fileName = null;//把圖片的url當做文件名,並進行MD5加密
   try {
    fileName = MD5Encoder.encode(url);
    File file=new File(CACHE_PATH,fileName);

    Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file));

    return bitmap;
   } catch (Exception e) {
    e.printStackTrace();
   }

   return null;
  }

  /**
   * 從網絡獲取圖片後,保存至本地緩存
   * @param url
   * @param bitmap
   */
  public void setBitmapToLocal(String url,Bitmap bitmap){
   try {
    String fileName = MD5Encoder.encode(url);//把圖片的url當做文件名,並進行MD5加密
    File file=new File(CACHE_PATH,fileName);

    //通過得到文件的父文件,判斷父文件是否存在
    File parentFile = file.getParentFile();
    if (!parentFile.exists()){
     parentFile.mkdirs();
    }

    //把圖片保存至本地
    bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(file));
   } catch (Exception e) {
    e.printStackTrace();
   }

  }
 }

4. 內存緩存(MemoryCacheUtils)

這是本文中最重要且需要重點介紹的部分

進行內存緩存,就一定要註意一個問題,那就是內存溢出(OutOfMemory)

為什麽會造成內存溢出?

    Android 虛擬機默認分配給每個App 16M的內存空間,真機會比16M大,但任會出現內存溢出的情況 Android 系統在加載圖片時是解析每一個像素的信息,再把每一個像素全部保存至內存中 圖片大小 = 圖片的總像素 * 每個像素占用的大小 > 單色圖:每個像素占用1/8個字節,16色圖:每個像素占用1/2個字節,256色圖:每個像素占用1個字節,24位圖:每個像素占用3個字節(常見的rgb構成的圖片) 例如一張1920x1080的JPG圖片,在Android 系統中是以ARGB格式解析的,即一個像素需占用4個字節,圖片的大小=1920x1080x4=7M

實現方法:

    通過 HashMap<String,Bitmap>鍵值對的方式保存圖片,key為地址,value為圖片對象,但因是強引用對象,很容易造成內存溢出,可以嘗試SoftReference軟引用對象 通過 HashMap<String, SoftReference<Bitmap>>SoftReference 為軟引用對象(GC垃圾回收會自動回收軟引用對象),但在Android2.3+後,系統會優先考慮回收弱引用對象,官方提出使用LruCache 通過 LruCache<String,Bitmap> least recentlly use 最少最近使用算法

會將內存控制在一定的大小內, 超出最大值時會自動回收, 這個最大值開發者自己定

 /**
  * 三級緩存之內存緩存
  */
 public class MemoryCacheUtils {

  // private HashMap<String,Bitmap> mMemoryCache=new HashMap<>();//1.因為強引用,容易造成內存溢出,所以考慮使用下面弱引用的方法
  // private HashMap<String, SoftReference<Bitmap>> mMemoryCache = new HashMap<>();//2.因為在Android2.3+後,系統會優先考慮回收弱引用對象,官方提出使用LruCache
  private LruCache<String,Bitmap> mMemoryCache;

  public MemoryCacheUtils(){
   long maxMemory = Runtime.getRuntime().maxMemory()/8;//得到手機最大允許內存的1/8,即超過指定內存,則開始回收
   //需要傳入允許的內存最大值,虛擬機默認內存16M,真機不一定相同
   mMemoryCache=new LruCache<String,Bitmap>((int) maxMemory){
    //用於計算每個條目的大小
    @Override
    protected int sizeOf(String key, Bitmap value) {
     int byteCount = value.getByteCount();
     return byteCount;
    }
   };

  }

  /**
   * 從內存中讀圖片
   * @param url
   */
  public Bitmap getBitmapFromMemory(String url) {
   //Bitmap bitmap = mMemoryCache.get(url);//1.強引用方法
   /*2.弱引用方法
   SoftReference<Bitmap> bitmapSoftReference = mMemoryCache.get(url);
   if (bitmapSoftReference != null) {
    Bitmap bitmap = bitmapSoftReference.get();
    return bitmap;
   }
   */
   Bitmap bitmap = mMemoryCache.get(url);
   return bitmap;

  }

  /**
   * 往內存中寫圖片
   * @param url
   * @param bitmap
   */
  public void setBitmapToMemory(String url, Bitmap bitmap) {
   //mMemoryCache.put(url, bitmap);//1.強引用方法
   /*2.弱引用方法
   mMemoryCache.put(url, new SoftReference<>(bitmap));
   */
   mMemoryCache.put(url,bitmap);
  }
 }

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持電腦玩物。


Tags: 緩存 圖片 網絡 三級 流量 通過

文章來源:


ads
ads

相關文章
ads

相關文章

ad