android 之加載圖片(二)

android 之加載圖片(二)

將單個Bitmap加載到UI是簡單直接的,但是如果我們需要一次性加載大量的圖片,事情則會變得複雜起來。在大多數情況下(例如在使用ListView,GridView或ViewPager時),屏幕上的圖片和因滑動將要顯示的圖片的數量通常是沒有限制的。

通過循環利用子視圖可以緩解內存的使用,垃圾回收器也會釋放那些不再需要使用的Bitmap。這些機制都非常好,但是爲了保證一個流暢的用戶體驗,我們希望避免在每次屏幕滑動回來時,都要重複處理那些圖片。內存與磁盤緩存通常可以起到輔助作用,允許控件可以快速地重新加載那些處理過的圖片。

這一課會介紹在加載多張Bitmap時使用內存緩存與磁盤緩存來提高響應速度與UI流暢度。

使用內存緩存(Use a Memory Cache)

內存緩存以花費寶貴的程序內存爲前提來快速訪問位圖。LruCache類(在API Level 4的Support Library中也可以找到)特別適合用來緩存Bitmaps,它使用一個強引用(strong referenced)的LinkedHashMap保存最近引用的對象,並且在緩存超出設置大小的時候剔除(evict)最近最少使用到的對象。

爲了給LruCache選擇一個合適的大小,需要考慮到下面一些因素:

應用剩下了多少可用的內存? 
多少張圖片會同時呈現到屏幕上?
設備的屏幕大小與密度是多少?
一個具有特別高密度屏幕(xhdpi)的設備,像Galaxy Nexus會比Nexus    S(hdpi)需要一個更大的緩存空間來緩存同樣數量的圖片。 
圖片被訪問的頻率如何?
爲不同的Bitmap組設置多個LruCache對象。    
某些時候保存大量低質量的Bitmap會非常有用,加載更高質量圖片的任務可以交給另外一個後臺線程。

通常沒有指定的大小或者公式能夠適用於所有的情形,我們需要分析實際的使用情況後,提出一個合適的解決方案。緩存太小會導致額外的花銷卻沒有明顯的好處,緩存太大同樣會導致java.lang.OutOfMemory的異常,並且使得你的程序只留下小部分的內存用來工作(緩存佔用太多內存,導致其他操作會因爲內存不夠而拋出異常)。

下面是一個爲Bitmap建立LruCache的示例

private void initLruCache() {

// 獲取程序運行的最大可用內存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

// 將程序可用內存的1/8大小分配給緩存
final int cacheSize = maxMemory / 8;

mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
    @Override
    protected int sizeOf(String key, Bitmap bitmap) {
    return bitmap.getByteCount() / 1024;
    }
};

}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}

當加載Bitmap顯示到ImageView 之前,會先從LruCache 中檢查是否存在這個Bitmap。如果確實存在,它會立即被用來顯示到ImageView上,如果沒有找到,會觸發一個後臺線程去處理顯示該Bitmap任務。

public void loadBitmap(int resId, ImageView imageView) {

// 將圖片的資源id當做map的key
final String imageKey = String.valueOf(resId);

// 先到緩存中查找
final Bitmap bitmap = getBitmapFromMemCache(imageKey);

// 如果該圖片已經被緩存了
if (bitmap != null) {

    // 那就直接拿來用
    imageView.setImageBitmap(bitmap);

    // 否則,新建任務,加載圖片
} else if (cancelPotentialWork(resId, imageView)) {

    final BitmapWorkerTask task = new BitmapWorkerTask(imageView);

    final AsyncDrawable asyncDrawable = new AsyncDrawable(
        getResources(), mPlaceHolderBitmap, task);

    // 你需要創建一個AsyncDrawable並且將它綁定到目標控件ImageView中:
    imageView.setImageDrawable(asyncDrawable);

    task.execute(resId);
}

}

使用磁盤緩存(Use a Disk Cache)

內存緩存能夠提高訪問最近用過的Bitmap的速度,但是我們無法保證最近訪問過的Bitmap都能夠保存在緩存中。像類似GridView等需要大量數據填充的控件很容易就會用盡整個內存緩存。另外,我們的應用可能會被類似打電話等行爲暫停從而退到後臺,一旦退到後臺,應用可能會被殺死,那麼內存緩存就會被銷燬,裏面的Bitmap也就不存在了。如果用戶恢復應用的狀態,那麼應用就需要重新處理那些圖片。
磁盤緩存可以用來保存那些已經處理過的Bitmap,它還可以減少那些不再內存緩存中的Bitmap的加載次數。當然從磁盤讀取圖片會比從內存要慢,而且由於磁盤讀取操作時間是不可預期的,讀取操作需要在後臺線程中處理。
磁盤緩存最核心的類是DiskLruCache類,這是一個android系統級的類,在框架中沒有。所以想使用DiskLruCache需要從android源碼中尋找這個類,由於這個非常的麻煩,所以推薦大家直接使用網上開源的項目就可以了,如https://github.com/JakeWharton/DiskLruCache
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章