本示例參考學習了一個國外的示例: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中。這樣可以系統設計得更具彈性。
// 0.75是加載因子爲經驗值,true則表示按照最近訪問量的高低排序,false則表示按照插入順序排序
private HashMap<String, Bitmap> mFirstLevelCache = new LinkedHashMap<String, Bitmap>(
MAX_CAPACITY / 2, 0.75f, true) {
private static final long serialVersionUID = 1L;
protected boolean removeEldestEntry(Entry<String, Bitmap> eldest) {
if (size() > MAX_CAPACITY) {// 當超過一級緩存閾值的時候,將老的值從一級緩存搬到二級緩存
mSecondLevelCache.put(eldest.getKey(),
new SoftReference<Bitmap>(eldest.getValue()));
return true;
}
return false;
};
};
/**
* 加載圖片,如果緩存中有就直接從緩存中拿,緩存中沒有就下載
* @param url
* @param adapter
* @param holder
*/
public void loadImage(String url, BaseAdapter adapter, ViewHolder holder) {
resetPurgeTimer();
Bitmap bitmap = getBitmapFromCache(url);// 從緩存中讀取
if (bitmap == null) {
holder.mImageView.setImageResource(R.drawable.ic_launcher);//緩存沒有設爲默認圖片
ImageLoadTask imageLoadTask = new ImageLoadTask();
imageLoadTask.execute(url, adapter, holder);//執行異步任務
} else {
holder.mImageView.setImageBitmap(bitmap);//設爲緩存圖片
}
}
public Bitmap getBitmapFromCache(String url) {
Bitmap bitmap = null;
bitmap = getFromFirstLevelCache(url);// 從一級緩存中拿
if (bitmap != null) {
return bitmap;
}
bitmap = getFromSecondLevelCache(url);//從二級緩存中拿
return bitmap;
}
private Bitmap getFromFirstLevelCache(String url) {
Bitmap bitmap = null;
synchronized (mFirstLevelCache) {
bitmap = mFirstLevelCache.get(url);
if (bitmap != null) {// 將最近訪問的元素放到鏈的頭部,提高下一次訪問該元素的檢索速度(LRU算法)
mFirstLevelCache.remove(url);
mFirstLevelCache.put(url, bitmap);
}
}
return bitmap;
}
private Bitmap getFromSecondLevelCache(String url) {
Bitmap bitmap = null;
SoftReference<Bitmap> softReference = mSecondLevelCache.get(url);
if (softReference != null) {
bitmap = softReference.get();
if (bitmap == null) {// 由於內存吃緊,軟引用已經被gc回收了
mSecondLevelCache.remove(url);
}
}
return bitmap;
}
// 定時清理緩存
private Runnable mClearCache = new Runnable() {
@Override
public void run() {
clear();
}
};
private Handler mPurgeHandler = new Handler();
// 重置緩存清理的timer
private void resetPurgeTimer() {
mPurgeHandler.removeCallbacks(mClearCache);
mPurgeHandler.postDelayed(mClearCache, DELAY_BEFORE_PURGE);
}
/**
* 清理緩存
*/
private void clear() {
mFirstLevelCache.clear();
mSecondLevelCache.clear();
}