Universal-Image-Loader解析(二)內(nèi)部緩存原理

Universal-Image-Loader解析系列

Universal-Image-Loader解析(一)基本介紹與使用
Universal-Image-Loader解析(二)內(nèi)部緩存原理
Universal-Image-Loader解析(三)源代碼解析

前篇文章則跟大家介紹了UIL的一些常用用法。

對(duì)于我們所知道的緩存霞篡,常用的是內(nèi)存緩存MemoryCache和硬盤緩存DiscCache玛追。一個(gè)讀取快容量小但两,一個(gè)讀取慢容量大胳施。

對(duì)于各自使用哪種緩存奇适,則可以在前面配置ImageLoaderConfiguration進(jìn)行緩存設(shè)置谜酒,當(dāng)然也可以自己自定義適合的緩存叹俏。

ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(this)  
        .memoryCache(new WeakMemoryCache())  
        .build();  

對(duì)于Universal-Image-Loader來(lái)說(shuō)它的緩存結(jié)構(gòu)也是分為內(nèi)存緩存MemoryCache和硬盤緩存DiskCache

一.MemoryCache內(nèi)存緩存

首先先看個(gè)結(jié)構(gòu)圖,理解UIL里面內(nèi)存緩存的結(jié)構(gòu)

MemoryCache

由于空間有限就沒畫成標(biāo)準(zhǔn)的UML類圖形式僻族。
對(duì)于基類MemoryCache它則是一個(gè)接口粘驰,里面定義了put,get圖片的方法

public interface MemoryCache {
    ...
    boolean put(String key, Bitmap value);
    
    Bitmap get(String key);

    Bitmap remove(String key);

    Collection<String> keys();

    void clear();
}

都是大家比較所熟悉的方法,而對(duì)于其他的類

我們一個(gè)個(gè)看

LruMemoryCache

這個(gè)類就是這個(gè)開源框架默認(rèn)的內(nèi)存緩存類述么,緩存的是bitmap的強(qiáng)引用蝌数。直接實(shí)現(xiàn)了MemoryCache方法

public class LruMemoryCache implements MemoryCache {

    private final LinkedHashMap<String, Bitmap> map;
    //最大容量
    private final int maxSize;
    /** 目前緩存的容量大小 */
    private int size;
    public LruMemoryCache(int maxSize) {
        ...
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
    }
    @Override
    public final Bitmap get(String key) {
        ...
        synchronized (this) {
            return map.get(key);
        }
    }
    @Override
    public final boolean put(String key, Bitmap value) {
        ...
        synchronized (this) {
            size += sizeOf(key, value);
            Bitmap previous = map.put(key, value);
            if (previous != null) {
                size -= sizeOf(key, previous);
            }
        }

        trimToSize(maxSize);
        return true;
    }

    /**
     * Lru算法,當(dāng)容量超過(guò)最大緩存容量度秘,則移除最久的條目
     */
    private void trimToSize(int maxSize) {
        while (true) {
            String key;
            Bitmap value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!");
                }

                if (size <= maxSize || map.isEmpty()) {
                    break;
                }

                Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
                if (toEvict == null) {
                    break;
                }
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                size -= sizeOf(key, value);
            }
        }
    }

    @Override
    public final Bitmap remove(String key) {
        ...
        synchronized (this) {
            Bitmap previous = map.remove(key);
            if (previous != null) {
                size -= sizeOf(key, previous);
            }
            return previous;
        }
    }
    ...
    //返回圖片的字節(jié)大小
    private int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }
    ...
}

LruMemoryCache的源碼也比較簡(jiǎn)單顶伞,內(nèi)部有個(gè)成員變量LinkedHashMap<String, Bitmap> map這里直接進(jìn)行保存的話則是強(qiáng)引用的形式。
主要看get,put方法剑梳。
對(duì)于get方法來(lái)說(shuō)唆貌,比較簡(jiǎn)單,直接根據(jù)指定的key返回對(duì)應(yīng)的圖片垢乙。
而對(duì)于put方法來(lái)說(shuō)锨咙,則需要考慮容量的問題。

@Override
    public final boolean put(String key, Bitmap value) {
        ...
        synchronized (this) {
            size += sizeOf(key, value);
            Bitmap previous = map.put(key, value);
            if (previous != null) {
                size -= sizeOf(key, previous);
            }
        }

        trimToSize(maxSize);
        return true;
    }

put方法首先調(diào)用了sizeof方法追逮,該方法則是返回指定Bitmap的字節(jié)大小酪刀,之后size +=,總緩存量增加钮孵,之后調(diào)用trimToSize該方法則是進(jìn)行緩存容量判斷的骂倘。

private void trimToSize(int maxSize) {
        while (true) {
            String key;
            Bitmap value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!");
                }

                if (size <= maxSize || map.isEmpty()) {
                    break;
                }

                Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
                if (toEvict == null) {
                    break;
                }
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                size -= sizeOf(key, value);
            }
        }
    }

如果加入后的size 緩存容量 <= maxSize 最大緩存容量,則直接break,不用進(jìn)行判定處理油猫。
如果大于的話稠茂,則直接移除最久未使用的。

大家肯定有疑問情妖,它到底怎么判斷最久未使用的睬关?沒看到相關(guān)代碼呀?

相信知道LinkedHashMap的話可能就知道毡证。
LinkedHashMap自身已經(jīng)實(shí)現(xiàn)了順序存儲(chǔ)电爹,默認(rèn)情況下是按照元素的添加順序存儲(chǔ),也可以啟用按照訪問順序存儲(chǔ)料睛,即最近讀取的數(shù)據(jù)放在最前面丐箩,最早讀取的數(shù)據(jù)放在最后面,然后它還有一個(gè)判斷是否刪除最老數(shù)據(jù)的方法恤煞,默認(rèn)是返回false屎勘,即不刪除數(shù)據(jù)。大家常見也就是按順序存儲(chǔ)居扒,很少忘了它還可以根據(jù)最近未使用的方法概漱。

//LinkedHashMap的一個(gè)構(gòu)造函數(shù),當(dāng)參數(shù)accessOrder為true時(shí)喜喂,即會(huì)按照訪問順序排序瓤摧,最近訪問的放在最前,最早訪問的放在后面
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
}

//LinkedHashMap自帶的判斷是否刪除最老的元素方法玉吁,默認(rèn)返回false照弥,即不刪除老數(shù)據(jù)
//我們要做的就是重寫這個(gè)方法,當(dāng)滿足一定條件時(shí)刪除老數(shù)據(jù)
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
}

回看我們前面LinkedHashMap的創(chuàng)建

  this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);

再舉個(gè)使用例子

例子

就比較明了了进副。

BaseMemoryCache

BaseMemoryCache同樣也是實(shí)現(xiàn)了MemoryCache方法这揣,不過(guò)它還是一個(gè)抽象類。
它是一個(gè)內(nèi)存緩存的基類影斑,實(shí)現(xiàn)了內(nèi)存緩存中常用的方法曾沈,只不過(guò)它里面提供了一個(gè)非強(qiáng)引用的Reference作為擴(kuò)展,方便GC的回收鸥昏,避免OOM.

public abstract class BaseMemoryCache implements MemoryCache {

    /** Stores not strong references to objects */
    private final Map<String, Reference<Bitmap>> softMap = Collections.synchronizedMap(new HashMap<String, Reference<Bitmap>>());

    @Override
    public Bitmap get(String key) {
        Bitmap result = null;
        Reference<Bitmap> reference = softMap.get(key);
        if (reference != null) {
            result = reference.get();
        }
        return result;
    }

    @Override
    public boolean put(String key, Bitmap value) {
        softMap.put(key, createReference(value));
        return true;
    }

    @Override
    public Bitmap remove(String key) {
        Reference<Bitmap> bmpRef = softMap.remove(key);
        return bmpRef == null ? null : bmpRef.get();
    }

    /** Creates {@linkplain Reference not strong} reference of value */
    protected abstract Reference<Bitmap> createReference(Bitmap value);
}

代碼也比較簡(jiǎn)單塞俱,內(nèi)存持有一個(gè)Map<String, Reference<Bitmap>> softMap來(lái)保存非強(qiáng)引用對(duì)象,具體的引用類型則看它實(shí)現(xiàn)的抽象方法createReference吏垮。

WeakMemoryCache

我們看它的一個(gè)子類WeakMemoryCache則是繼承與BaseMemory障涯,實(shí)現(xiàn)createReference

public class WeakMemoryCache extends BaseMemoryCache {
    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}

很明顯是來(lái)保存弱引用對(duì)象的。

LimitedMemoryCache

我們看它的另外一個(gè)子類LimitedMemoryCache膳汪,但它并沒有實(shí)現(xiàn)BaseMemoryCache里的createReference方法唯蝶,它也是一個(gè)抽象類,在BaseMemoryCache基礎(chǔ)上封裝了個(gè)抽象方法
protected abstract Bitmap removeNext();用來(lái)處理當(dāng)緩存容量不足時(shí)的情況遗嗽。

public abstract class LimitedMemoryCache extends BaseMemoryCache {
    ...
    //當(dāng)前保存的Bitmap粘我,用來(lái)統(tǒng)計(jì)緩存數(shù)
    private final List<Bitmap> hardCache = Collections.synchronizedList(new LinkedList<Bitmap>());
    ...
    @Override
    public boolean put(String key, Bitmap value) {
        boolean putSuccessfully = false;
        // Try to add value to hard cache
        int valueSize = getSize(value);
        int sizeLimit = getSizeLimit();
        int curCacheSize = cacheSize.get();
        if (valueSize < sizeLimit) {
            while (curCacheSize + valueSize > sizeLimit) {
                Bitmap removedValue = removeNext();
                if (hardCache.remove(removedValue)) {
                    curCacheSize = cacheSize.addAndGet(-getSize(removedValue));
                }
            }
            hardCache.add(value);
            cacheSize.addAndGet(valueSize);

            putSuccessfully = true;
        }
        // Add value to soft cache
        super.put(key, value);
        return putSuccessfully;
    }

    @Override
    public Bitmap remove(String key) {
        Bitmap value = super.get(key);
        if (value != null) {
            if (hardCache.remove(value)) {
                cacheSize.addAndGet(-getSize(value));
            }
        }
        return super.remove(key);
    }
   ...
    protected abstract int getSize(Bitmap value);

    protected abstract Bitmap removeNext();
}

可以看到在 LimitedMemoryCache里面又有一個(gè)List<Bitmap>保存的是強(qiáng)引用,而在BaseMemoryCache里面也有個(gè)Map<String, Reference<Bitmap>> softMap來(lái)保存Bitmap,為什么要這樣征字。

這主要是因?yàn)樵?code>BaseMemoryCache里面并沒有做緩存限制處理都弹,它只是封裝實(shí)現(xiàn)了基本的Bitmap的put,get。而當(dāng)面對(duì)緩存容量有限的情況下匙姜,則需要交給子類去處理畅厢。

我們看下這里的put方法,關(guān)鍵在

while (curCacheSize + valueSize > sizeLimit) {
                Bitmap removedValue = removeNext();
                if (hardCache.remove(removedValue)) {
                    curCacheSize = cacheSize.addAndGet(-getSize(removedValue));
                }
            }

當(dāng)超過(guò)容量時(shí)氮昧,調(diào)用抽象方法removeNext由子類自行實(shí)現(xiàn)框杜,之后hardCache移除,但此時(shí)并沒有調(diào)用softMap的移除袖肥。

也就是對(duì)于List<Bitmap>來(lái)說(shuō)咪辱,當(dāng)它的緩存容量超過(guò)的時(shí)候,它會(huì)移除第一個(gè)對(duì)象來(lái)緩解容量椎组,但是保存在Map<String, Reference<Bitmap>> softMap里面的Bitmap并沒有被移除油狂。
如果這樣下去softMap豈不是會(huì)無(wú)限大?

這是因?yàn)樵?code>Map<String, Reference<Bitmap>> softMap里面保存的Bitmap是弱引用的存在庐杨,而在List<Bitmap>里面保存的是強(qiáng)引用选调,當(dāng)內(nèi)存不足的時(shí)候,GC則會(huì)先清除softMap里面的對(duì)象灵份。

FIFOLimitedMemoryCache

我們看下LimitedMemoryCache的一個(gè)子類FIFOLimitedMemoryCache仁堪,看到FIFO也就是先進(jìn)先出了。

public class FIFOLimitedMemoryCache extends LimitedMemoryCache {

    private final List<Bitmap> queue = Collections.synchronizedList(new LinkedList<Bitmap>());
    ...
    @Override
    public boolean put(String key, Bitmap value) {
        if (super.put(key, value)) {
            queue.add(value);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public Bitmap remove(String key) {
        Bitmap value = super.get(key);
        if (value != null) {
            queue.remove(value);
        }
        return super.remove(key);
    }
    ...
    @Override
    protected Bitmap removeNext() {
        return queue.remove(0);
    }

    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}

可以看到同樣的這里也有個(gè)List<Bitmap> queue來(lái)保存記錄填渠,而在removeNext那里弦聂,返回的正是隊(duì)列的第一個(gè)元素,符合FIFO氛什。

LRULimitedMemoryCache

再來(lái)看一個(gè)另外一個(gè)子類LRULimitedMemoryCache也就是最近未使用刪除莺葫。

public class LRULimitedMemoryCache extends LimitedMemoryCache {

    /** Cache providing Least-Recently-Used logic */
    private final Map<String, Bitmap> lruCache = Collections.synchronizedMap(new LinkedHashMap<String, Bitmap>(INITIAL_CAPACITY, LOAD_FACTOR, true));
   ...
    @Override
    protected Bitmap removeNext() {
        Bitmap mostLongUsedValue = null;
        synchronized (lruCache) {
            Iterator<Entry<String, Bitmap>> it = lruCache.entrySet().iterator();
            if (it.hasNext()) {
                Entry<String, Bitmap> entry = it.next();
                mostLongUsedValue = entry.getValue();
                it.remove();
            }
        }
        return mostLongUsedValue;
    }

    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}

可以看到,這里的LRU處理則是使用LinkedHashMap枪眉,在它的構(gòu)造方法中第三個(gè)參數(shù)為true表示使用LRU捺檬,之后再removeNext返回那個(gè)Bitmap。

同理其他子類也如下贸铜,就不一一列舉堡纬。

MemoryCache小結(jié)

1. 只使用的是強(qiáng)引用緩存

  • LruMemoryCache(這個(gè)類就是這個(gè)開源框架默認(rèn)的內(nèi)存緩存類,緩存的是bitmap的強(qiáng)引用)

2.使用強(qiáng)引用和弱引用相結(jié)合的緩存有

  • UsingFreqLimitedMemoryCache(如果緩存的圖片總量超過(guò)限定值蒿秦,先刪除使用頻率最小的bitmap)

  • LRULimitedMemoryCache(這個(gè)也是使用的lru算法烤镐,和LruMemoryCache不同的是,他緩存的是bitmap的弱引用)

  • FIFOLimitedMemoryCache(先進(jìn)先出的緩存策略棍鳖,當(dāng)超過(guò)設(shè)定值炮叶,先刪除最先加入緩存的bitmap)

  • LargestLimitedMemoryCache(當(dāng)超過(guò)緩存限定值,先刪除最大的bitmap對(duì)象)

  • LimitedAgeMemoryCache(當(dāng) bitmap加入緩存中的時(shí)間超過(guò)我們?cè)O(shè)定的值,將其刪除)

3.只使用弱引用緩存

  • WeakMemoryCache(這個(gè)類緩存bitmap的總大小沒有限制镜悉,唯一不足的地方就是不穩(wěn)定祟辟,緩存的圖片容易被回收掉)

二.DiskCache硬盤緩存

同樣先來(lái)看個(gè)結(jié)構(gòu)

DiskCache

DiskCache的設(shè)計(jì)其實(shí)和MemoryCache一樣,對(duì)于基類DiskCache积瞒,它同樣是一個(gè)接口

public interface DiskCache {
    //返回硬盤緩存的根目錄
    File getDirectory();

    File get(String imageUri);

    boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException;

    boolean save(String imageUri, Bitmap bitmap) throws IOException;

    boolean remove(String imageUri);

    void close();

    void clear();
}

同樣一個(gè)個(gè)看

LruDiskCache

LruDiskCache則是直接實(shí)現(xiàn)了DiskCache接口川尖,采用LRU算法來(lái)進(jìn)行緩存處理登下。
再理解LruDiskCache前茫孔,先理解另一個(gè)類DiskLruCache

final class DiskLruCache implements Closeable {
    static final String JOURNAL_FILE = "journal";
    static final String JOURNAL_FILE_TEMP = "journal.tmp";
    static final String JOURNAL_FILE_BACKUP = "journal.bkp";
    static final String MAGIC = "libcore.io.DiskLruCache";
    ...
    private final LinkedHashMap<String, Entry> lruEntries =
            new LinkedHashMap<String, Entry>(0, 0.75f, true);
    ...
    final ThreadPoolExecutor executorService =
            new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
    ...
    public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize, int maxFileCount)
            throws IOException {
        ...
    }
    ...
    public synchronized Snapshot get(String key) throws IOException {
        ...
    }
    ...
    private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
        ...
    }
    /** A snapshot of the values for an entry. */
    public final class Snapshot implements Closeable {
        private final String key;
        private final long sequenceNumber;
        private File[] files;
        private final InputStream[] ins;
        private final long[] lengths;
        ...
    }
    ...
    public final class Editor {
        private final Entry entry;
        private final boolean[] written;
        private boolean hasErrors;
        private boolean committed;
        ...
    }
    ...
    private final class Entry {
        private final String key;

        private final long[] lengths;

        private boolean readable;

        private Editor currentEditor;

        private long sequenceNumber;
        ...
    }

這個(gè)DiskLruCache比較長(zhǎng)也比較復(fù)雜,它是LruDiskCache的一個(gè)文件工具類被芳。這里的緩存數(shù)據(jù)存儲(chǔ)在文件系統(tǒng)上的一個(gè)目錄缰贝。
同時(shí)也注意到這里的一個(gè)成員變量
private final LinkedHashMap<String, Entry> lruEntries =new LinkedHashMap<String, Entry>(0, 0.75f, true);
可以知道這是用來(lái)處理LRU的。

同時(shí)這里的value則是Entry畔濒,Entry則是封裝了當(dāng)前文件的編輯情況Ediotr以及key剩晴。
而這里Editor封裝了文件的寫入情況OutputStreamSnapshot封裝了文件的讀取情況InputStream侵状。

回頭看回LruDiskCache

public class LruDiskCache implements DiskCache {
    protected DiskLruCache cache;
    private File reserveCacheDir;

    protected final FileNameGenerator fileNameGenerator;
    ...
    public LruDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator, long cacheMaxSize,
            int cacheMaxFileCount) throws IOException {
        ...
        this.reserveCacheDir = reserveCacheDir;
        this.fileNameGenerator = fileNameGenerator;
        initCache(cacheDir, reserveCacheDir, cacheMaxSize, cacheMaxFileCount);
    }

    private void initCache(File cacheDir, File reserveCacheDir, long cacheMaxSize, int cacheMaxFileCount)
        ...
            cache = DiskLruCache.open(cacheDir, 1, 1, cacheMaxSize, cacheMaxFileCount);
        ...
    }
    @Override
    public File get(String imageUri) {
        DiskLruCache.Snapshot snapshot = null;
        try {
            snapshot = cache.get(getKey(imageUri));
            return snapshot == null ? null : snapshot.getFile(0);
        } 
        ...
    }
    @Override
    public boolean save(String imageUri, Bitmap bitmap) throws IOException {
        DiskLruCache.Editor editor = cache.edit(getKey(imageUri));
        ...
        OutputStream os = new BufferedOutputStream(editor.newOutputStream(0), bufferSize);
        boolean savedSuccessfully = false;
        try {
            savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os);
        }
        ...
        return savedSuccessfully;
    }

首先LruDiskCache內(nèi)部成員變量帶有DiskLruCache還有文件的保存目錄等赞弥,在它的構(gòu)造方法中調(diào)用DiskLruCache.open方法創(chuàng)建了DiskLruCache對(duì)象,而在它的open方法里趣兄,則根據(jù)文件的目錄情況創(chuàng)建了對(duì)應(yīng)的文件系統(tǒng)绽左。

再看它的save方法,先調(diào)用getKey方法將uri轉(zhuǎn)換為對(duì)應(yīng)的key艇潭,而在cache,edit中

private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
        ...
        Entry entry = lruEntries.get(key);
        if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null
                || entry.sequenceNumber != expectedSequenceNumber)) {
            return null; // Snapshot is stale.
        }
        if (entry == null) {
            entry = new Entry(key);
            lruEntries.put(key, entry);
        } else if (entry.currentEditor != null) {
            return null; // Another edit is in progress.
        }

        Editor editor = new Editor(entry);
        entry.currentEditor = editor;
        ...
        return editor;
    }

則是根據(jù)指定的key先判斷緩存文件中有沒有相應(yīng)的key拼窥,如果沒有則創(chuàng)建一個(gè)Entry對(duì)象持有它,之后保存在lruEntries之后蹋凝,創(chuàng)建一個(gè)當(dāng)前Entry的編輯對(duì)象Editor鲁纠,以便之后寫入到文件中。

s之后調(diào)用了

        OutputStream os = new BufferedOutputStream(editor.newOutputStream(0), bufferSize);

editor.newOutputStream則是根據(jù)當(dāng)前目錄和key創(chuàng)建出一個(gè)文件鳍寂,之后打開這個(gè)文件的一個(gè)輸出流情況改含,獲取到之后就進(jìn)行Bitmap的寫入。

同理迄汛,看下LruDiskCache的get方法

@Override
    public File get(String imageUri) {
        DiskLruCache.Snapshot snapshot = null;
        try {
            snapshot = cache.get(getKey(imageUri));
            return snapshot == null ? null : snapshot.getFile(0);
        } 
        ...
    }

調(diào)用了cache,get

public synchronized Snapshot get(String key) throws IOException {
        捍壤。。隔心。
        Entry entry = lruEntries.get(key);
        ...
        File[] files = new File[valueCount];
        InputStream[] ins = new InputStream[valueCount];
        try {
            File file;
            for (int i = 0; i < valueCount; i++) {
                file = entry.getCleanFile(i);
                files[i] = file;
                ins[i] = new FileInputStream(file);
            }
        } 
        ...
        return new Snapshot(key, entry.sequenceNumber, files, ins, entry.lengths);
    }

在get方法中白群,先根據(jù)key拿到對(duì)應(yīng)的Entry,再拿到對(duì)應(yīng)的文件打開輸入流硬霍,之后傳入到Snapshot帜慢,
而在snapshot.getFile

/** Returns file with the value for {@code index}. */
        public File getFile(int index) {
            return files[index];
        }

返回的則是對(duì)應(yīng)的文件。

BaseDiskCache

BaseDiskCache同樣也是直接實(shí)現(xiàn)了DiskCache方法,實(shí)現(xiàn)的方法也比較簡(jiǎn)單

public abstract class BaseDiskCache implements DiskCache {
    ...
    protected final File cacheDir;
    protected final File reserveCacheDir;

    protected final FileNameGenerator fileNameGenerator;
    
    public BaseDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {
        ...
        this.cacheDir = cacheDir;
        this.reserveCacheDir = reserveCacheDir;
        this.fileNameGenerator = fileNameGenerator;
    }
    
    @Override
    public boolean save(String imageUri, Bitmap bitmap) throws IOException {
        File imageFile = getFile(imageUri);
        File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);
        OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);
        boolean savedSuccessfully = false;
        try {
            savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os);
        } finally {
            IoUtils.closeSilently(os);
            if (savedSuccessfully && !tmpFile.renameTo(imageFile)) {
                savedSuccessfully = false;
            }
            if (!savedSuccessfully) {
                tmpFile.delete();
            }
        }
        bitmap.recycle();
        return savedSuccessfully;
    }
    
    @Override
    public File get(String imageUri) {
        return getFile(imageUri);
    }
    
    protected File getFile(String imageUri) {
        String fileName = fileNameGenerator.generate(imageUri);
        File dir = cacheDir;
        if (!cacheDir.exists() && !cacheDir.mkdirs()) {
            if (reserveCacheDir != null && (reserveCacheDir.exists() || reserveCacheDir.mkdirs())) {
                dir = reserveCacheDir;
            }
        }
        return new File(dir, fileName);
    }

比較簡(jiǎn)單粱玲,根據(jù)對(duì)應(yīng)的文件去打開獲取躬柬。它的兩個(gè)子類LimitedAgeDiskCacheUnlimitedDiskCache也都不一一擴(kuò)展開了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末抽减,一起剝皮案震驚了整個(gè)濱河市允青,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卵沉,老刑警劉巖颠锉,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異史汗,居然都是意外死亡琼掠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門停撞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)瓷蛙,“玉大人,你說(shuō)我怎么就攤上這事戈毒〖桠” “怎么了?”我有些...
    開封第一講書人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵埋市,是天一觀的道長(zhǎng)冠桃。 經(jīng)常有香客問我,道長(zhǎng)恐疲,這世上最難降的妖魔是什么腊满? 我笑而不...
    開封第一講書人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮培己,結(jié)果婚禮上碳蛋,老公的妹妹穿的比我還像新娘。我一直安慰自己省咨,他們只是感情好肃弟,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著零蓉,像睡著了一般笤受。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敌蜂,一...
    開封第一講書人閱讀 52,549評(píng)論 1 312
  • 那天箩兽,我揣著相機(jī)與錄音,去河邊找鬼章喉。 笑死汗贫,一個(gè)胖子當(dāng)著我的面吹牛身坐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播落包,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼部蛇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了咐蝇?” 一聲冷哼從身側(cè)響起涯鲁,我...
    開封第一講書人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎有序,沒想到半個(gè)月后抹腿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡笔呀,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年幢踏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了髓需。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片许师。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖僚匆,靈堂內(nèi)的尸體忽然破棺而出微渠,到底是詐尸還是另有隱情,我是刑警寧澤咧擂,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布逞盆,位于F島的核電站,受9級(jí)特大地震影響松申,放射性物質(zhì)發(fā)生泄漏云芦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一贸桶、第九天 我趴在偏房一處隱蔽的房頂上張望舅逸。 院中可真熱鬧,春花似錦皇筛、人聲如沸琉历。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)旗笔。三九已至,卻和暖如春拄踪,著一層夾襖步出監(jiān)牢的瞬間蝇恶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工惶桐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留撮弧,地道東北人的猛。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像想虎,于是被迫代替她去往敵國(guó)和親卦尊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容