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中。這樣可以系統設計得更具彈性。

  1. // 0.75是加載因子爲經驗值,true則表示按照最近訪問量的高低排序,false則表示按照插入順序排序

  2. private HashMap<String, Bitmap> mFirstLevelCache = new LinkedHashMap<String, Bitmap>(

  3.                        MAX_CAPACITY / 2, 0.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.        };      

複製代碼
加載圖片:先讀緩存,緩存麼有就開啓異步任務從網絡下載
  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.        }

複製代碼
讀取緩存的代碼:
  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.        }

複製代碼
定期清理緩存
  1. // 定時清理緩存

  2.        private Runnable mClearCache = new Runnable() {

  3.                @Override

  4.                public void run() {

  5.                        clear();

  6.                }

  7.        };

  8.        private Handler mPurgeHandler = new Handler();


  9.        // 重置緩存清理的timer

  10.        private void resetPurgeTimer() {

  11.                mPurgeHandler.removeCallbacks(mClearCache);

  12.                mPurgeHandler.postDelayed(mClearCache, DELAY_BEFORE_PURGE);

  13.        }


  14.        /**

  15.         * 清理緩存

  16.         */

  17.        private void clear() {

  18.                mFirstLevelCache.clear();

  19.                mSecondLevelCache.clear();

  20.        }

複製代碼
總結:這篇文章主要講了圖片的緩存技巧,拿來主義,學習從別人的代碼中吸取精華,代碼我也上傳了,並且附有詳細的註釋,這裏的緩存都是在內存當中,適合短期有效的緩存,如果是長期有效的圖片,我們可以採用文件存儲的方式,再設一級文件緩存,有興趣的同學可以研究一下。感謝您耐心的看完了,送您一杯咖啡 150658w2snwznloq89g8qk.gif
115322777fa6h8om6f7n69.png


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章