1. 程式人生 > >史上最全Universal-Image-Loader原始碼解析————快取篇

史上最全Universal-Image-Loader原始碼解析————快取篇

背景

在我寫下這篇部落格的時候,我還是一名二本學校計算機專業大四的應屆畢業生,自學Android開發快兩年了,這兩年的時間裡面,其實真的是感慨萬千,兩年的時間裡面,Android的很多事情也都見識過了,其實Android對於新手入門在就業方面是相當不友好的事情。都說第一個吃螃蟹的人最好,是的,Android從2012年那段時間火起來,那時候Android開發會個ListView都被大廠強著要,時過境遷,現在隨著大批中小創業公司的倒臺,微信一個App的小程式都壟斷絕大部分應用,再加上在大前端 WebApp的出現,混合開發,跨平臺等開發的出現,現在很多初級中級Android程式設計師真沒有就業出路,一個崗位企業面對成百上千的開發者應聘,當然也就挑挑揀揀,面試的題目也算是越來越難,從Java基礎到多執行緒,到開源框架的原始碼設計,等等都會考察到,沒辦法上了這條賊船,也只能坐下去了,下面先帶大家瞭解第一個開源框架ImageLoader 相信這個開源框架應該是最常用也是用到最多的開源框架了。

一、原始碼框架

關於這個開源框架的專案架構我直接上圖更加清晰,可以看到分為記憶體相關,工具類相關,和核心程式碼相關

這裡寫圖片描述

二、記憶體快取和磁碟快取的相關

可以看到分為記憶體快取和磁碟快取

這裡寫圖片描述

三、記憶體快取相關的類與操作

下面是記憶體快取的每一個類所代表的意思和作用,我們隨後將逐個進行每個類每個方法的講解。

這裡寫圖片描述

(1) MemoryCahce 介面

介面中定義了正刪改查等方法,詳情看圖

這裡寫圖片描述

(2)baseMemoryCache實現介面的一個基類

這個基類定義了 一個弱引用還有就是實現增刪改查的方法

這裡寫圖片描述

(3)LimitedMemoryCache

這個類繼承了BaseMemoryCache這個類 也就是說也是使用弱引用,限制最大記憶體為16M,重點在於,新增快取的邏輯,詳細看程式碼 詳情看註解

public abstract class LimitedMemoryCache extends BaseMemoryCache {
    private static final int MAX_NORMAL_CACHE_SIZE_IN_MB = 16;
    private static final int MAX_NORMAL_CACHE_SIZE = MAX_NORMAL_CACHE_SIZE_IN_MB * 1024 * 1024
; //限制圖片大小為16M最大 private final int sizeLimit; //限制數 private final AtomicInteger cacheSize; /** * Contains strong references to stored objects. Each next object is added last. If hard cache size will exceed * limit then first object is deleted (but it continue exist at {@link #softMap} and can be collected by GC at any * time) */ private final List<Bitmap> hardCache = Collections.synchronizedList(new LinkedList<Bitmap>()); /** @param sizeLimit Maximum size for cache (in bytes) */ public LimitedMemoryCache(int sizeLimit) { this.sizeLimit = sizeLimit; cacheSize = new AtomicInteger(); if (sizeLimit > MAX_NORMAL_CACHE_SIZE) { } } //新增圖片到快取 @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); } //清空 @Override public void clear() { hardCache.clear(); cacheSize.set(0); super.clear(); } // protected int getSizeLimit() { return sizeLimit; } protected abstract int getSize(Bitmap value); protected abstract Bitmap removeNext(); }

(4)WeakMemoryCache 只使用弱引用 因為他重寫了父類BaseMemoryCache 的createReference 方法 而父類在放入快取的時候會呼叫這個方法這樣就把強引用弄成弱引用,在記憶體不夠的時候自動被回收,各個方法的詳情看註解

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

(5)UsingFreqLimitedMemoryCache

LimitedMemoryCache這個是一個最近使用最少的快取策略用一個Map

public class UsingFreqLimitedMemoryCache extends LimitedMemoryCache {
    private final Map<Bitmap, Integer> usingCounts = Collections.synchronizedMap(new HashMap<Bitmap, Integer>());
    public UsingFreqLimitedMemoryCache(int sizeLimit) {
        super(sizeLimit);
    }
    @Override
    public boolean put(String key, Bitmap value) {
        if (super.put(key, value)) {//呼叫父類的放入快取方法
            usingCounts.put(value, 0);//記錄這個為0
            return true;
        } else {
            return false;
        }
    }
    @Override
    public Bitmap get(String key) {
        Bitmap value = super.get(key);
        // Increment usage count for value if value is contained in hardCahe
        if (value != null) {//拿出如果有物件,那麼把這個物件使用次數加1
            Integer usageCount = usingCounts.get(value);
            if (usageCount != null) {
                usingCounts.put(value, usageCount + 1);
            }
        }
        return value;
    }
    @Override
    public Bitmap remove(String key) {//刪除就刪除掉,使用記錄也隨之刪除掉了
        Bitmap value = super.get(key);
        if (value != null) {
            usingCounts.remove(value);
        }
        return super.remove(key);
    }
    @Override
    public void clear() { //清空就將記錄清空 呼叫父類清空
        usingCounts.clear();
        super.clear();
    }
    @Override
    protected int getSize(Bitmap value) {//拿到圖片的大小
        return value.getRowBytes() * value.getHeight();
    }
    @Override
    protected Bitmap removeNext() {//獲取刪除下一個 這個是遍歷記錄集合把使用最少的標記出來,在父類刪除掉,這個記錄也將刪除掉
        Integer minUsageCount = null;
        Bitmap leastUsedValue = null;
        Set<Entry<Bitmap, Integer>> entries = usingCounts.entrySet();
        synchronized (usingCounts) {
            for (Entry<Bitmap, Integer> entry : entries) {
                if (leastUsedValue == null) {
                    leastUsedValue = entry.getKey();
                    minUsageCount = entry.getValue();
                } else {
                    Integer lastValueUsage = entry.getValue();
                    if (lastValueUsage < minUsageCount) {
                        minUsageCount = lastValueUsage;
                        leastUsedValue = entry.getKey();
                    }
                }
            }
        }
        usingCounts.remove(leastUsedValue);
        return leastUsedValue;
    }
    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {//強行轉化為弱引用
        return new WeakReference<Bitmap>(value);
    }
}

(6)LruMemoryCache最近最少使用記憶體策略

也是預設的快取策略,在下一章可能會提到配置引數的時候,其實他就說實現了一個MemoryCache介面 實現增刪改查的方法,初始化了一個強引用 LinkedHashMap 這個資料結構的特點就說會記錄最近使用的頻率具體的方法實現看程式碼註解

public class LruMemoryCache implements MemoryCache {
    private final LinkedHashMap<String, Bitmap> map;
    private final int maxSize;
    /** Size of this cache in bytes */
    private int size;
    /** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
    public LruMemoryCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
    }
    /**
     根據鍵值直接從map中拿到對應大小
     */
    @Override
    public final Bitmap get(String key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }
        synchronized (this) {
            return map.get(key);
        }
    }
    //新增的方法
    @Override
    public final boolean put(String key, Bitmap value) {
        if (key == null || value == null) {
            throw new NullPointerException("key == null || value == null");
        }
        synchronized (this) {
            size += sizeOf(key, value);//呼叫sizeOf這個函式獲得該bitmap物件的佔用記憶體的大小,並且讓快取總數增加
            Bitmap previous = map.put(key, value);//這裡就是把物件放入容器中的最核心的一句程式碼,如果容器中已經有了此元素,則返回該元素的value值,否則返回空
            if (previous != null) {
                size -= sizeOf(key, previous);//如果容器中已經有了此元素,則需要把增加的數量減掉
            }
        }
        trimToSize(maxSize);
        return true;
    }
    /**此函式計算是否超出最大限量,是則刪除隊尾元素
     */
    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);
            }
        }
    }
      //刪除的方法 使用同步程式碼塊 刪除從map 當然size要減掉相應大小
    @Override
    public final Bitmap remove(String key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }
        synchronized (this) {
            Bitmap previous = map.remove(key);
            if (previous != null) {
                size -= sizeOf(key, previous);
            }
            return previous;
        }
    }
//拿到所有的鍵
    @Override
    public Collection<String> keys() {
        synchronized (this) {
            return new HashSet<String>(map.keySet());
        }
    }
      //*=清楚的方法傳一個負一
    @Override
    public void clear() {
        trimToSize(-1); // -1 will evict 0-sized elements
    }

    /**算出大小
     */
    private int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }
    @Override
    public synchronized final String toString() {
        return String.format("LruCache[maxSize=%d]", maxSize);
    }
}

(7)LRULimitedMemoryCache這個和剛剛LruMemoryCache,基本相似只不過,它繼承的是LimitedMemoryCache,使用的是弱引用,這裡就不多說了直接看程式碼詳細方法細節看註解

public class LRULimitedMemoryCache extends LimitedMemoryCache {
    private static final int INITIAL_CAPACITY = 10;
    private static final float LOAD_FACTOR = 1.1f;
    /** Cache providing Least-Recently-Used logic */
    private final Map<String, Bitmap> lruCache = Collections.synchronizedMap(new LinkedHashMap<String, Bitmap>(INITIAL_CAPACITY, LOAD_FACTOR, true));
    /** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
    public LRULimitedMemoryCache(int maxSize) {//設定最大值
        super(maxSize);
    }
    @Override
    public boolean put(String key, Bitmap value) {//新增的方法重寫
        if (super.put(key, value)) {
            lruCache.put(key, value);
            return true;
        } else {
            return false;
        }
    }
    @Override
    public Bitmap get(String key) {//重寫直接從父類中拿
        lruCache.get(key); // call "get" for LRU logic
        return super.get(key);
    }
    @Override
    public Bitmap remove(String key) {//刪除 直接從父類刪除當然本地的也要刪除
        lruCache.remove(key);
        return super.remove(key);
    }
    @Override
    public void clear() {
        lruCache.clear();
        super.clear();
    }
    @Override
    protected int getSize(Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }
    @Override
    protected Bitmap removeNext() {//拿到LinkHashMap值最後一個也就是使用次數最少的哪一個給父類去刪除
        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;
    }

    /**
     * 重寫導致弱引用
     * @param value
     * @return
     */
    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}

(8)LimitedAgeMemoryCache

傳入一個介面和一個生命值,把時間到的快取給刪除掉,詳細看程式碼方法註解

/*******************************************************************************
 * Copyright 2011-2014 Sergey Tarasevich
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package leakcanary.imageloader.cache.memory.impl;

import android.graphics.Bitmap;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import leakcanary.imageloader.cache.memory.MemoryCache;

/**當 bitmap加入快取中的時間超過我們設定的值,將其刪除
 */
public class LimitedAgeMemoryCache implements MemoryCache {
    private final MemoryCache cache;//介面 可以看成BaseMemoryCache這樣的類
    private final long maxAge;//最大生命值
    private final Map<String, Long> loadingDates = Collections.synchronizedMap(new HashMap<String, Long>());
   //一個是鍵值一個生命時間
    /**初始化
     */
    public LimitedAgeMemoryCache(MemoryCache cache, long maxAge) {
        this.cache = cache;
        this.maxAge = maxAge * 1000; // to milliseconds
    }

    /**
     放入呼叫介面的儲存方法 ,然後記錄最新的方法
     */
    @Override
    public boolean put(String key, Bitmap value) {
        boolean putSuccesfully = cache.put(key, value);
        if (putSuccesfully) {
            loadingDates.put(key, System.currentTimeMillis());
        }
        return putSuccesfully;
    }
//根據鍵值拿到 如果超過了有效期就刪除掉
    @Override
    public Bitmap get(String key) {
        Long loadingDate = loadingDates.get(key);
        if (loadingDate != null && System.currentTimeMillis() - loadingDate > maxAge) {
            cache.remove(key);
            loadingDates.remove(key);
        }
        return cache.get(key);
    }
     //刪除掉
    @Override
    public Bitmap remove(String key) {
        loadingDates.remove(key);
        return cache.remove(key);
    }

    @Override
    public Collection<String> keys() {
        return cache.keys();
    }
//清除掉
    @Override
    public void clear() {
        cache.clear();
        loadingDates.clear();
    }
}

(9)LargestLimitedMemoryCache

LimitedMemoryCache繼承LimitedMemoryCache,其實就是拿一個集合 Map

public class LargestLimitedMemoryCache extends LimitedMemoryCache {
    //map圖片大小和bitmap
    private final Map<Bitmap, Integer> valueSizes = Collections.synchronizedMap(new HashMap<Bitmap, Integer>());

    public LargestLimitedMemoryCache(int sizeLimit) {
        super(sizeLimit);
    }

    //直接放進去key是 計算大小
    @Override
    public boolean put(String key, Bitmap value) {
        if (super.put(key, value)) {
            valueSizes.put(value, getSize(value));
            return true;
        } else {
            return false;
        }
    }
    //刪除就記錄大小的集合也刪除
    @Override
    public Bitmap remove(String key) {
        Bitmap value = super.get(key);
        if (value != null) {
            valueSizes.remove(value);
        }
        return super.remove(key);
    }
 //清除
    @Override
    public void clear() {
        valueSizes.clear();
        super.clear();
    }
//拿到大小
    @Override
    protected int getSize(Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }
//拿到最大的那個返回回去父類刪除掉
    @Override
    protected Bitmap removeNext() {
        Integer maxSize = null;
        Bitmap largestValue = null;
        Set<Entry<Bitmap, Integer>> entries = valueSizes.entrySet();
        synchronized (valueSizes) {
            for (Entry<Bitmap, Integer> entry : entries) {
                if (largestValue == null) {
                    largestValue = entry.getKey();
                    maxSize = entry.getValue();
                } else {
                    Integer size = entry.getValue();
                    if (size > maxSize) {
                        maxSize = size;
                        largestValue = entry.getKey();
                    }
                }
            }
        }
        valueSizes.remove(largestValue);
        return largestValue;
    }
//使用弱引用
    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}

(10)FIFOLimitedMemoryCache先進先出快取策略

其實就維護了一個List queue

/*******************************************************************************
 * Copyright 2011-2014 Sergey Tarasevich
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package leakcanary.imageloader.cache.memory.impl;

import android.graphics.Bitmap;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import leakcanary.imageloader.cache.memory.LimitedMemoryCache;

/**
 * 先進先出快取策略
 * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to
 * exceed size limit. When cache reaches limit size then cache clearing is processed by FIFO principle.<br />
 * <br />
 * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
 * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @since 1.0.0
 */
public class FIFOLimitedMemoryCache extends LimitedMemoryCache {
    //構建一個佇列 用一個線性列表
    private final List<Bitmap> queue = Collections.synchronizedList(new LinkedList<Bitmap>());

    public FIFOLimitedMemoryCache(int sizeLimit) {
        super(sizeLimit);
    }

    //增加直接往這個佇列中增加
    @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
    public void clear() {
        queue.clear();
        super.clear();
    }
     //拿到一個圖片的大小
    @Override
    protected int getSize(Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }
   //刪除佇列頭
    @Override
    protected Bitmap removeNext() {
        return queue.remove(0);
    }
//使用弱引用
    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}

好了所有記憶體快取策略的每個方法和實現原理都說了現在看磁碟快取了

三、磁碟快取的操作與架構

我們先來看一下磁碟快取的每個類的架構,後面將針對每個類來進行分析

這裡寫圖片描述

(1)DiskCache 一個介面定義了檔案的各種操作 詳細看程式碼註解

public interface DiskCache {
    /**
    拿到磁碟的目錄路勁
     */
    File getDirectory();
    /**
       根據uri得到檔案
     */
    File get(String imageUri);
    /**
    儲存圖片和url
     */
    boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException;
    /**
        儲存圖片
     */
    boolean save(String imageUri, Bitmap bitmap) throws IOException;
    /**
     根據uri刪除.
     */
    boolean remove(String imageUri);
    /** 關閉資源 */
    void close();
    /** 清空*/
    void clear();
}

(2)FileNameGenerator

根據Uri生成唯一檔案命名的介面,下面分別是使用HashCode實現和MD5實現

public interface FileNameGenerator {

    /** 生成由URI定義的影象的唯一檔名。 */
    String generate(String imageUri);
}
/**
 使用hashCode的方式生成唯一檔名
 */
public class HashCodeFileNameGenerator implements FileNameGenerator {
    @Override
    public String generate(String imageUri) {
        return String.valueOf(imageUri.hashCode());
    }
}
/**
 *根據uri 根據MD5的方式生成為唯一檔名
 */
public class Md5FileNameGenerator implements FileNameGenerator {
    private static final String HASH_ALGORITHM = "MD5";
    private static final int RADIX = 10 + 26; // 10 digits + 26 letters
    @Override
    public String generate(String imageUri) {
        byte[] md5 = getMD5(imageUri.getBytes());
        BigInteger bi = new BigInteger(md5).abs();
        return bi.toString(RADIX);
    }
    private byte[] getMD5(byte[] data) {
        byte[] hash = null;
        try {
            MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
            digest.update(data);
            hash = digest.digest();
        } catch (NoSuchAlgorithmException e) {
        }
        return hash;
    }
}

(2)BaseDiskCache 磁碟快取類的一個比較重要的方法 下面兩個子類基本是實現父類的方法


public abstract class BaseDiskCache implements DiskCache {
//BUFFER大小
    public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 Kb
    /** {@value */
    public static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG;
    /** {@value */
    public static final int DEFAULT_COMPRESS_QUALITY = 100;

    private static final String ERROR_ARG_NULL = " argument must be not null";
    private static final String TEMP_IMAGE_POSTFIX = ".tmp";//臨時檔案的副檔名

    protected final File cacheDir;////快取地址
    protected final File reserveCacheDir;//cache//備用地址

    protected final FileNameGenerator fileNameGenerator;//檔名生成器

    protected int bufferSize = DEFAULT_BUFFER_SIZE;

    protected Bitmap.CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT;
    protected int compressQuality = DEFAULT_COMPRESS_QUALITY;

    /** 一個引數 傳地址*/
    public BaseDiskCache(File cacheDir) {
        this(cacheDir, null);
    }

    /**檔案地址 和備用地址 如果為空就傳用HashCode生成命名
     */
    public BaseDiskCache(File cacheDir, File reserveCacheDir) {
        this(cacheDir, reserveCacheDir, DefaultConfigurationFactory.createFileNameGenerator());
    }

    /**
可以傳遞三個引數的
     */
    public BaseDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {
        if (cacheDir == null) {
            throw new IllegalArgumentException("cacheDir" + ERROR_ARG_NULL);
        }
        if (fileNameGenerator == null) {
            throw new IllegalArgumentException("fileNameGenerator" + ERROR_ARG_NULL);
        }
        this.cacheDir = cacheDir;
        this.reserveCacheDir = reserveCacheDir;
        this.fileNameGenerator = fileNameGenerator;
    }
     //拿到路勁
    @Override
    public File getDirectory() {
        return cacheDir;
    }
    //根據uri拿到檔案
    @Override
    public File get(String imageUri) {
        return getFile(imageUri);
    }
//將流中資料儲存到檔案中,先把內容寫到臨時檔案中,然後再重新命名為目標檔案
    @Override
    public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {
        File imageFile = getFile(imageUri);
        File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);
        boolean loaded = false;
        try {
            OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);
            try {
                loaded = IoUtils.copyStream(imageStream, os, listener, bufferSize);
            } finally {
                IoUtils.closeSilently(os);
            }
        } finally {
            if (loaded && !tmpFile.renameTo(imageFile)) {
                loaded = false;
            }
            if (!loaded) {
                tmpFile.delete();
            }
        }
        return loaded;
    }
    //儲存圖片,也是採用先寫到臨時檔案在重新命名的策略,這個可以借鑑
    @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 boolean remove(String imageUri) {
        return getFile(imageUri).delete();
    }

    @Override
    public void close() {
        // Nothing to do
    }
//清空資料夾
    @Override
    public void clear() {
        File[] files = cacheDir.listFiles();
        if (files != null) {
            for (File f : files) {
                f.delete();
            }
        }
    }
    /**
     * 將傳過來的uri用檔名生成器搞一下拿到這個圖片地址所在的資源
     * */
    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);
    }
//返回BufferSize
    public void setBufferSize(int bufferSize) {
        this.bufferSize = bufferSize;
    }

    public void setCompressFormat(Bitmap.CompressFormat compressFormat) {
        this.compressFormat = compressFormat;
    }
    public void setCompressQuality(int compressQuality) {
        this.compressQuality = compressQuality;
    }
}

(4)UnlimitedDiskCache

沒有限制的磁碟快取方法,基本上是呼叫父類的方法就好了

public class UnlimitedDiskCache extends BaseDiskCache {
    public UnlimitedDiskCache(File cacheDir) {
        super(cacheDir);
    }
    public UnlimitedDiskCache(File cacheDir, File reserveCacheDir) {
        super(cacheDir, reserveCacheDir);
    }
    public UnlimitedDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {