Android異步加載 緩存第一章

今天要講的緩存策略(緩存層分為三層:內(nèi)存層,磁盤層,網(wǎng)絡(luò)層)。
當我們第一次打開應用獲取圖片時,先到網(wǎng)絡(luò)去下載圖片,然后依次存入內(nèi)存緩存,磁盤緩存,當我們再一次需要用到剛才下載的這張圖片時,就不需要再重復的到網(wǎng)絡(luò)上去下載,直接可以從內(nèi)存緩存和磁盤緩存中找,由于內(nèi)存緩存速度較快,我們優(yōu)先到內(nèi)存緩存中尋找該圖片,如果找到則運用,如果沒有找到(內(nèi)存緩存大小有限),那么我們再到磁盤緩存中去找。只要我們合理的去協(xié)調(diào)這三層緩存運用,便可以提升應用性能和用戶體驗。
1、內(nèi)存層:(手機內(nèi)存)
內(nèi)存緩存相對于磁盤緩存而言,速度要來的快很多,但缺點容量較小且會被系統(tǒng)回收,這里的實現(xiàn)我用到了LruCache。LruCache這個類是Android3.1版本中提供的,如果你是在更早的Android版本中開發(fā),則需要導入android-support-v4的jar包。
2、磁盤層:(SD卡)
相比內(nèi)存緩存而言速度要來得慢很多,但容量很大,這里的實現(xiàn)我用到了DiskLruCache類。DiskLruCache是非Google官方編寫,但獲得官方認證的硬盤緩存類,該類沒有限定在Android內(nèi),所以理論上java應用也可以使用DiskLreCache來緩存。這是DiskLruCache類的下載地址:http://pan.baidu.com/s/1o6tPjz8
3、網(wǎng)絡(luò)層:(移動網(wǎng)絡(luò),無線網(wǎng)絡(luò))

原理

Android原生為我們提供了一個LruCache,其中維護著一個LinkedHashMap。LruCache可以用來存儲各種類型的數(shù)據(jù),但最常見的是存儲圖片(Bitmap)。LruCache創(chuàng)建LruCache時,我們需要設(shè)置它的大小,一般是系統(tǒng)最大存儲空間的八分之一。LruCache的機制是存儲最近、最后使用的圖片,如果LruCache中的圖片大小超過了其默認大小,則會將最老、最遠使用的圖片移除出去。
當圖片被LruCache移除的時候,我們需要手動將這張圖片添加到軟引用(SoftReference)中。我們需要在項目中維護一個由SoftReference組成的集合,其中存儲被LruCache移除出來的圖片。軟引用的一個好處是當系統(tǒng)空間緊張的時候,軟引用可以隨時銷毀,因此軟引用是不會影響系統(tǒng)運行的,換句話說,如果系統(tǒng)因為某個原因OOM了,那么這個原因肯定不是軟引用引起的。

下面敘述一下三級緩存的流程:

當我們的APP中想要加載某張圖片時,先去LruCache中尋找圖片,如果LruCache中有,則直接取出來使用,如果LruCache中沒有,則去SoftReference中尋找,如果SoftReference中有,則從SoftReference中取出圖片使用,同時將圖片重新放回到LruCache中,如果SoftReference中也沒有圖片,則去文件系統(tǒng)中尋找,如果有則取出來使用,同時將圖片添加到LruCache中,如果沒有,則連接網(wǎng)絡(luò)從網(wǎng)上下載圖片。圖片下載完成后,將圖片保存到文件系統(tǒng)中,然后放到LruCache中。
LruCache子類ImageCache:

import android.graphics.Bitmap;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.LruCache;

import java.lang.ref.SoftReference;
import java.util.Map;

/**
 * 圖片緩存
 */
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR1)
public class ImageCache extends LruCache<String, Bitmap> {
    private Map<String, SoftReference<Bitmap>> cacheMap;

    public ImageCache(Map<String, SoftReference<Bitmap>> cacheMap) {
        super((int) (Runtime.getRuntime().maxMemory() / 8));
        this.cacheMap = cacheMap;
    }

    @Override // 獲取圖片大小
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }

    @Override // 當有圖片從LruCache中移除時,將其放進軟引用集合中
    protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
        if (oldValue != null) {
            SoftReference<Bitmap> softReference = new SoftReference<Bitmap>(oldValue);
            cacheMap.put(key, softReference);
        }
    }

    public Map<String, SoftReference<Bitmap>> getCacheMap() {
        return cacheMap;
    }
}

三級緩存工具類ImageCacheLoader:

  import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.widget.ImageView;

import java.io.File;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;

/**
 * 緩存工具類
 */
public class CacheUtil {
    private static CacheUtil instance;

    private Context context;
    private ImageCache imageCache;

    private CacheUtil(Context context) {
        this.context = context;
        Map<String, SoftReference<Bitmap>> cacheMap = new HashMap<>();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { // SDK版本判斷
            this.imageCache = new ImageCache(cacheMap);
        }
    }

    public static CacheUtil getInstance(Context context) {
        if (instance == null) {
            synchronized (CacheUtil.class) {
                if (instance == null) {
                    instance = new CacheUtil(context);
                }
            }
        }
        return instance;
    }

    /**
     * 將圖片添加到緩存中
     */
    private void putBitmapIntoCache(String fileName, byte[] data) {
        // 將圖片的字節(jié)數(shù)組寫入到內(nèi)存中
        FileUtil.getInstance(context).writeFileToStorage(fileName, data);
        // 將圖片存入強引用(LruCache)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
            imageCache.put(fileName, BitmapFactory.decodeByteArray(data, 0, data.length));
        }
    }

    /**
     * 從緩存中取出圖片
     */
    private Bitmap getBitmapFromCache(String fileName) {
        // 從強引用(LruCache)中取出圖片
        Bitmap bm = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) { // SDK版本判斷
            bm = imageCache.get(fileName);
            if (bm == null) {
                // 如果圖片不存在強引用中,則去軟引用(SoftReference)中查找
                Map<String, SoftReference<Bitmap>> cacheMap = imageCache.getCacheMap();
                SoftReference<Bitmap> softReference = cacheMap.get(fileName);
                if (softReference != null) {
                    bm = softReference.get();
                    imageCache.put(fileName, bm);
                } else {
                    // 如果圖片不存在軟引用中,則去內(nèi)存中找
                    byte[] data = FileUtil.getInstance(context).readBytesFromStorage(fileName);
                    if (data != null && data.length > 0) {
                        bm = BitmapFactory.decodeByteArray(data, 0, data.length);
                        imageCache.put(fileName, bm);
                    }
                }
            }
        }
        return bm;
    }

    /**
     * 使用三級緩存為ImageView設(shè)置圖片
     */
    public void setImageToView(final String path, final ImageView view) {
        final String fileName = path.substring(path.lastIndexOf(File.separator) + 1);
        Bitmap bm = getBitmapFromCache(fileName);
        if (bm != null) {
            view.setImageBitmap(bm);
        } else {
            // 從網(wǎng)絡(luò)獲取圖片
            new Thread(new Runnable() {
                @Override
                public void run() {
                    byte[] b = HttpUtil.getInstance().getByteArrayFromWeb(path);
                    if (b != null && b.length > 0) {
                        // 將圖片字節(jié)數(shù)組寫入到緩存中
                        putBitmapIntoCache(fileName, b);
                        final Bitmap bm = BitmapFactory.decodeByteArray(b, 0, b.length);
                        // 將從網(wǎng)絡(luò)獲取到的圖片設(shè)置給ImageView
                        view.post(new Runnable() {
                            @Override
                            public void run() {
                                view.setImageBitmap(bm);
                            }
                        });
                    }
                }
            }).start();
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容