圖片緩存之內存緩存技術LruCache,軟引用

每當碰到一些大圖片的時候,我們如果不對圖片進行處理就會報OOM異常,
這個
問題曾經讓我覺得很煩惱,後來終於得到了解決,
那麼現在就讓我和大家一起分享一下吧。
這篇博文要講的圖片緩存機制,我接觸到的有兩鍾,一種是軟引用,另一種是內存緩存技術。
先來看下兩者的使用方式,再來作比較。
除了加載圖片時要用到緩存處理,還有一個比較重要的步驟要做,就是要先壓縮圖片。

1、壓縮圖片
至於要壓縮到什麼狀態就要看自己當時的處境了,壓縮圖片的時候既要達到一個小的值,又不能讓其模糊
,更不能拉伸圖片。

  1. /**
  2.          * 加載內存卡圖片
  3.          */
  4.         BitmapFactory.Options options = new BitmapFactory.Options();
  5.         options.inJustDecodeBounds = true; // 設置了此屬性一定要記得將值設置爲false
  6.         Bitmap bitmap = null;
  7.         bitmap = BitmapFactory.decodeFile(url, options);
  8.         int be = (int) ((options.outHeight > options.outWidth ? options.outHeight / 150
  9.                 : options.outWidth / 200));
  10.         if (be <= 0) // 判斷200是否超過原始圖片高度
  11.             be = 1; // 如果超過,則不進行縮放
  12.         options.inSampleSize = be;
  13.         options.inPreferredConfig = Bitmap.Config.ARGB_4444;
  14.         options.inPurgeable = true;
  15.         options.inInputShareable = true;
  16.         options.inJustDecodeBounds = false;
  17.         try {
  18.             bitmap = BitmapFactory.decodeFile(url, options);
  19.         } catch (OutOfMemoryError e) {
  20.             System.gc();
  21.             Log.e(TAG, "OutOfMemoryError");
  22.         }


2、軟引用:
只要有足夠的內存,就一直保持對象,直到發現內存吃緊且沒有
Strong Ref時纔回收對象。
我們可以這樣定義:map裏面的鍵是用來放圖片地址的,既可以是網絡上的圖片地址,也可以SDcard上的圖片地址
map裏面的值裏面放的是持有軟引用的Bitmap,當然如果你要放Drawable,那也是可以的。

  1. private Map<String, SoftReference<Bitmap>> imageMap 
  2.                                            = new HashMap<String, SoftReference<Bitmap>>();
接下來就讓我再介紹一下如何具體加載圖片:
步驟:(1)先通過URL查看緩存中是否有圖片,如果有,則直接去緩存中取得。
           如果沒有,就開線程重新去網上下載。
      (2)下載完了之後,就把圖片放在緩存裏面,方便下次可以直接從緩存中取得。
  1. public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) {
  2.         SoftReference<Bitmap> reference = imageMap.get(imageUrl);
  3.         if(reference != null) {
  4.             if(reference.get() != null) {
  5.                 return reference.get();
  6.             }
  7.         }
  8.         final Handler handler = new Handler() {
  9.             public void handleMessage(final android.os.Message msg) {
  10.                 //加入到緩存中
  11.                 Bitmap bitmap = (Bitmap)msg.obj;
  12.                 imageMap.put(imageUrl, new SoftReference<Bitmap>(bitmap));
  13.                 if(imageCallBack != null) {
  14.                     imageCallBack.getBitmap(bitmap);
  15.                 }
  16.             }
  17.         };
  18.         new Thread(){
  19.             public void run() {
  20.                 Message message = handler.obtainMessage();
  21.                 message.obj = downloadBitmap(imageUrl);
  22.                 handler.sendMessage(message);
  23.             }
  24.         }.start();
  25.         return null ;
  26.     }

  27.     // 從網上下載圖片
  28.     private Bitmap downloadBitmap (String imageUrl) {
  29.         Bitmap bitmap = null;
  30.         try {
  31.             bitmap = BitmapFactory.decodeStream(new URL(imageUrl).openStream());
  32.             return bitmap ;
  33.         } catch (Exception e) {
  34.             e.printStackTrace();
  35.             return null;
  36.         } 
  37.     }
  1.     public interface ImageCallBack{
  2.         void getBitmap(Bitmap bitmap);
  3.     }


2、內存緩存技術
另外一種圖片緩存的方式就是內存緩存技術。在Android中,有一個叫做LruCache類專門用來做圖片緩存處理的。
它有一個特點,當緩存的圖片達到了預先設定的值的時候,那麼近期使用次數最少的圖片就會被回收掉
步驟:(1)要先設置緩存圖片的內存大小,我這裏設置爲手機內存的1/8,
           手機內存的獲取方式:int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024);
      (2)LruCache裏面的鍵值對分別是URL和對應的圖片
      (3)重寫了一個叫做sizeOf的方法,返回的是圖片數量。

  1. private LruCache<String, Bitmap> mMemoryCache;
  2. private LruCacheUtils() {
  3.         if (mMemoryCache == null)
  4.             mMemoryCache = new LruCache<String, Bitmap>(
  5.                     MAXMEMONRY / 8) {
  6.                 @Override
  7.                 protected int sizeOf(String key, Bitmap bitmap) {
  8.                     // 重寫此方法來衡量每張圖片的大小,默認返回圖片數量。
  9.                     return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
  10.                 }

  11.                 @Override
  12.                 protected void entryRemoved(boolean evicted, String key,
  13.                         Bitmap oldValue, Bitmap newValue) {
  14.                     Log.v("tag", "hard cache is full , push to soft cache");
  15.                    
  16.                 }
  17.             };
  18.     }
     (4)下面的方法分別是清空緩存、添加圖片到緩存、從緩存中取得圖片、從緩存中移除。
          移除和清除緩存是必須要做的事,因爲圖片緩存處理不當就會報內存溢出,所以一定要引起注意。
  1. public void clearCache() {
  2.         if (mMemoryCache != null) {
  3.             if (mMemoryCache.size() > 0) {
  4.                 Log.d("CacheUtils",
  5.                         "mMemoryCache.size() " + mMemoryCache.size());
  6.                 mMemoryCache.evictAll();
  7.                 Log.d("CacheUtils", "mMemoryCache.size()" + mMemoryCache.size());
  8.             }
  9.             mMemoryCache = null;
  10.         }
  11.     }

  12.     public synchronized void addBitmapToMemoryCache(String key, Bitmap bitmap) {
  13.         if (mMemoryCache.get(key) == null) {
  14.             if (key != null && bitmap != null)
  15.                 mMemoryCache.put(key, bitmap);
  16.         } else
  17.             Log.w(TAG, "the res is aready exits");
  18.     }

  19.     public synchronized Bitmap getBitmapFromMemCache(String key) {
  20.         Bitmap bm = mMemoryCache.get(key);
  21.         if (key != null) {
  22.             return bm;
  23.         }
  24.         return null;
  25.     }

  26.     /**
  27.      * 移除緩存
  28.      * 
  29.      * @param key
  30.      */
  31.     public synchronized void removeImageCache(String key) {
  32.         if (key != null) {
  33.             if (mMemoryCache != null) {
  34.                 Bitmap bm = mMemoryCache.remove(key);
  35.                 if (bm != null)
  36.                     bm.recycle();
  37.             }
  38.         }
  39.     }

4、兩者的比
說到這裏,我覺得有必要來進行一下比較了。
網上有很多人使用軟引用加載圖片的多 ,但是現在已經不再推薦使用這種方式了,
(1)因爲從 Android 2.3 (API Level 9)開始,垃圾回收器會更傾向於回收持有軟引用或弱引用的對象,
     這讓軟引用和弱引用變得不再可靠。

(2)另外,Android 3.0 (API Level 11)中,圖片的數據會存儲在本地的內存當中,
     因而無法用一種可預見的方式將其釋放,這就有潛在的風險造成應用程序的內存溢出並崩潰,

所以我這裏用得是LruCache來緩存圖片,當存儲Image的大小大於LruCache設定的值,系統自動釋放內存,
這個類是3.1版本中提供的,如果你是在更早的Android版本中開發,則需要導入android-support-v4的jar包

發佈了14 篇原創文章 · 獲贊 3 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章