Android網絡請求6--解析Glide緩存機制

1. 簡介

Glide緩存分爲兩部分,分別爲內存緩存和硬盤緩存。其中內存緩存的主要作用是防止應用重複地將圖片數據讀取到內存中,而硬盤緩存的主要作用是防止應用重複從網絡或其他地方重複下載和讀取數據。

內存緩存和硬盤緩存的配合才實現了Glide極佳的圖片緩存效果。

2. 緩存Key

大家都知道,如果想緩存一個東西的話,必然會有對應的緩存key。那麼Glide的緩存key在哪呢?

首先我們來看下Engine.load()方法:

public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
        DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
        Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
    // 判斷是否爲主線程
    Util.assertMainThread();
    // 獲取當前時間
    long startTime = LogTime.getLogTime();

    // 生成key,包括id、width、height、signature以及decoder等參數
    final String id = fetcher.getId();
    EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
            loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
            transcoder, loadProvider.getSourceEncoder());
    
    // -----------讀取內存緩存-----------
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
        cb.onResourceReady(cached);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Loaded resource from cache", startTime, key);
        }
        return null;
    }

    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
        cb.onResourceReady(active);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Loaded resource from active resources", startTime, key);
        }
        return null;
    }

    EngineJob current = jobs.get(key);
    if (current != null) {
        current.addCallback(cb);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Added to existing load", startTime, key);
        }
        return new LoadStatus(cb, current);
    }

    // -----------runnable中會先讀取磁盤緩存-----------
    EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
    DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
            transcoder, diskCacheProvider, diskCacheStrategy, priority);
    EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
    jobs.put(key, engineJob);
    engineJob.addCallback(cb);
    engineJob.start(runnable);

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);
}

我們現在需要關注的核心就是10-13行的代碼。可以看到,他在10行調用了fetcher.getId()方法獲得了一個id字符串,這個字符串也是我們要加載圖片的唯一標識,比如說我們要從網絡讀取,那圖片的Url就是他的id。

接下來,通過id、signature、width、height等一些列參數傳入EngineKeyFactory的buildKey()方法當中,從而構建出了一個EngineKey對象。這個對象就是Glide的緩存key了。

3. 內存緩存

首先我們來看下Glide如何使用圖片緩存。

3.1 用法

內存緩存Glide是默認開啓的。

也就是說,當我們使用Glide加載一張圖片之後,這張圖片就會被緩存到內存當中,只要她還沒從內存中被清除,下次使用Glide加載這張圖片都會直接從內存中讀取。這樣無疑可以大幅度提升圖片的加載效率。

那麼既然是默認開啓的,怎樣纔可以關閉他呢?

Glide對此也提供了方法:

Glide.with(this)
     .load(url)
     .skipMemoryCache(true)
     .into(imageView);

通過skipMemoryCache() API 就可以關閉它。

3.2 源碼

說起緩存,大家一定第一反應就是LRUCache。沒錯,內存緩存就是通過LRUCache實現的,但是他除了LRUCache之外,還有一種弱引用緩存。那就讓我們通過源碼來分析下:

還記得我們在講Glide請求源碼load()的時候,分析到在loadGeneric()方法中會調用Glide.buildStreamModelLoader()方法來回去一個ModelLoader對象,我們現在來看下他的源碼:

public static <T> ModelLoader<T, InputStream> buildStreamModelLoader(Class<T> modelClass, Context context) {
    return buildModelLoader(modelClass, InputStream.class, context);
}

可以看到這個方法直接調用了buildModelLoader()方法,我們再來看看這個方法:

public static <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass,
        Context context) {
     if (modelClass == null) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Unable to load null model, setting placeholder only");
        }
        return null;
    }
    return Glide.get(context).getLoaderFactory().buildModelLoader(modelClass, resourceClass);
}

核心代碼就是return那一句,可以看到他先調用了一個Glide.get()方法,而這個方法就是關鍵:

public static Glide get(Context context) {
    // 懶漢單例模式
    if (glide == null) {
        synchronized (Glide.class) {
            if (glide == null) {
                // 構造對象之前的一些配置
                Context applicationContext = context.getApplicationContext();
                List<GlideModule> modules = new ManifestParser(applicationContext).parse();
                GlideBuilder builder = new GlideBuilder(applicationContext);
                for (GlideModule module : modules) {
                    module.applyOptions(applicationContext, builder);
                }
                // 創建Glide對象
                glide = builder.createGlide();
                for (GlideModule module : modules) {
                    module.registerComponents(applicationContext, glide);
                }
            }
        }
    }
    return glide;
}

這個方法基本上就是一個單例模式獲取對象的常規寫法。可以直接看14行,glide的構造方式就是調用builder.createGlide()方法,我們再來看下這個方法:

Glide createGlide() {
    if (sourceService == null) {
        final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
        sourceService = new FifoPriorityThreadPoolExecutor(cores);
    }
    if (diskCacheService == null) {
        diskCacheService = new FifoPriorityThreadPoolExecutor(1);
    }

    MemorySizeCalculator calculator = new MemorySizeCalculator(context);
    // 創建BitmapPool
    if (bitmapPool == null) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            int size = calculator.getBitmapPoolSize();
            bitmapPool = new LruBitmapPool(size);
        } else {
            bitmapPool = new BitmapPoolAdapter();
        }
    }

    // 創建內存緩存
    if (memoryCache == null) {
        memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
    }

    // 創建硬盤緩存
    if (diskCacheFactory == null) {
        diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }

    // 創建engine
    if (engine == null) {
        engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
    }

    // 創建DecodeFormat
    if (decodeFormat == null) {
        decodeFormat = DecodeFormat.DEFAULT;
    }

    // 返回創建好的Glide對象
    return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}

目前我們只需要關注內存緩存部分。第23行創建了一個LruResourceCache對象,並賦值給了memoryCache。這個就是Glide實現內存緩存所用的LruCache對象了。到這緩存的準備工作就做好了。

接下來我們繼續回到Engine的load()方法:

public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
        DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
        Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
    
    // ... 省略部分代碼
    
    // -----------讀取內存緩存-----------
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
        cb.onResourceReady(cached);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Loaded resource from cache", startTime, key);
        }
        return null;
    }

    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
        cb.onResourceReady(active);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Loaded resource from active resources", startTime, key);
        }
        return null;
    }

    EngineJob current = jobs.get(key);
    if (current != null) {
        current.addCallback(cb);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Added to existing load", startTime, key);
        }
        return new LoadStatus(cb, current);
    }

    // -----------runnable中會先讀取磁盤緩存-----------
    EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
    DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
            transcoder, diskCacheProvider, diskCacheStrategy, priority);
    EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
    jobs.put(key, engineJob);
    engineJob.addCallback(cb);
    engineJob.start(runnable);

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);
}

在第8行調用了loadFromCache()方法來獲取緩存圖片,並通過cb.onResourceReady()方法進行回調;如果沒有獲取到,就會在17行調用loadFromActiveResources()來獲取緩存圖片。只有在兩個方法都獲取不到的時候纔會繼續向下執行,從而開啓線程加載圖片。

也就是說,load()方法會通過兩個方式來獲取內存緩存:loadFromCache()loadFromActiveResources()。這兩個方法一個是LruCache,一個是弱引用。

public class Engine implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {
    
    // ...
    
    private final MemoryCache cache;
    private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
    
    // ...
    
    // 從activeResources中取出圖片
    private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
        if (!isMemoryCacheable) {
            return null;
        }

        EngineResource<?> active = null;
        WeakReference<EngineResource<?>> activeRef = activeResources.get(key);
        if (activeRef != null) {
            active = activeRef.get();
            if (active != null) {
                active.acquire();
            } else {
                activeResources.remove(key);
            }
        }

        return active;
    }

    private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
        // 判斷內存緩存是否被禁用
        if (!isMemoryCacheable) {
            return null;
        }

        // 通過key調用getEngineResourceFromCache方法來獲取緩存,見下面
        EngineResource<?> cached = getEngineResourceFromCache(key);
        if (cached != null) {
            cached.acquire();
            // 又存入activeResources中
            // activeResources就是一個弱引用的HashMap,用來緩存正在使用的圖片
            activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
        }
        return cached;
    }
    
    @SuppressWarnings("unchecked")
    private EngineResource<?> getEngineResourceFromCache(Key key) {
        // 根據key找到對應緩存並取出移除
        Resource<?> cached = cache.remove(key);

        final EngineResource result;
        // 根據取出的cached來構建result
        if (cached == null) {
            result = null;
        } else if (cached instanceof EngineResource) {
            // Save an object allocation if we've cached an EngineResource (the typical case).
            result = (EngineResource) cached;
        } else {
            result = new EngineResource(cached, true /*isCacheable*/);
        }
        // 將result返回
        return result;
    }
    
    // ...
}

緩存步驟應該先從loadFromCache()方法開啓,他首先調用getEngineResourceFromCache()方法獲取緩存。這個方法是根據傳入的key然後取出緩存,接着判斷如果緩存是EngineResource的對象的話就將cached造型成EngineResource的對象並返回,否則的話直接根據cached調用EngineResource的構造方法返回result。

現在我們回到loadFromCache()方法,我們得到緩存後,在把它存入activeResources。activeResources就是弱引用的HashMap。由此可知,loadFromActiveResources()方法就是從這個activeResources中取值。由於它是弱引用緩存,所以他可以保護這些圖片不會被LruCache算法回收掉。

所以大致流程就是先從內存中讀取緩存,讀取出來了就把它從內存中移除並存入ActiveResources弱引用緩存,接着直接從弱引用緩存中取。如果內存中讀取不到的話,就開啓線程從網絡中獲取。

但是,回想我們之前的研究過程,發現我們分析了緩存是怎麼被取出來的,但是沒有說緩存是什麼時候被存進去的。

還記得我們之前說過,在圖片加載完成之後,會在EngineJob中通過Handler發送一條消息將執行邏輯切回到主線程中,從而執行handleResultOnMainThread()方法。現在我們來重新看下這個方法:

private void handleResultOnMainThread() {
    // 是否取消
    if (isCancelled) {
        resource.recycle();
        return;
    } else if (cbs.isEmpty()) {
        throw new IllegalStateException("Received a resource without any callbacks to notify");
    }
    
    // 構建EngineResource對象
    engineResource = engineResourceFactory.build(resource, isCacheable);
    hasResource = true;

    // Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it
    // synchronously released by one of the callbacks.
    engineResource.acquire();
    // 回調
    listener.onEngineJobComplete(key, engineResource);

    for (ResourceCallback cb : cbs) {
        if (!isInIgnoredCallbacks(cb)) {
            engineResource.acquire();
            cb.onResourceReady(engineResource);
        }
    }
    // Our request is complete, so we can release the resource.
    engineResource.release();
}

在第11行中,通過engineResourceFactory的build()方法構建出EngineResource對象,並在第18行通過回調返回到了Engine的onEngineJobComplete()方法中去:

@Override
public void onEngineJobComplete(Key key, EngineResource<?> resource) {
    Util.assertMainThread();
    // A null resource indicates that the load failed, usually due to an exception.
    if (resource != null) {
        resource.setResourceListener(key, this);

        if (resource.isCacheable()) {
            // 存入ActiveResources
            activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
        }
    }
    // TODO: should this check that the engine job is still current?
    jobs.remove(key);
}

可以看到在這個方法中,第10行,就被put進了activeResources中。

這塊我們加載到的圖片就存入了弱引用緩存中。

但是我們什麼時候存入LruCache中的呢?

回到剛剛的handleResultOnMainThread()方法。16行和22行分別調用了EngineResource的acquire()方法,在第27行調用了他的release()方法。

那我們來看下這兩個方法:

class EngineResource<Z> implements Resource<Z> {

    private int acquired;

    void acquire() {
        if (isRecycled) {
            throw new IllegalStateException("Cannot acquire a recycled resource");
        }
        if (!Looper.getMainLooper().equals(Looper.myLooper())) {
            throw new IllegalThreadStateException("Must call acquire on the main thread");
        }
        // 核心就是讓acquired這個變量++
        ++acquired;
    }
    
        void release() {
        if (acquired <= 0) {
            throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
        }
        if (!Looper.getMainLooper().equals(Looper.myLooper())) {
            throw new IllegalThreadStateException("Must call release on the main thread");
        }
        // 同樣核心就是讓acquired這個變量--
        if (--acquired == 0) {
            // 回調,見下面
            listener.onResourceReleased(key, this);
        }
    }
}

/**
 * Engine.onResourceReleased()
 */
@Override
public void onResourceReleased(Key cacheKey, EngineResource resource) {
    Util.assertMainThread();
    // 根據key從activeResources中移除
    activeResources.remove(cacheKey);
    // 存入LruCache中
    if (resource.isCacheable()) {
        cache.put(cacheKey, resource);
    } else {
        resourceRecycler.recycle(resource);
    }
}

可以看到,Engine維護了一個對象,叫做acquired,然後acquire()方法會讓這個變量加1,release()方法會讓它減1。

如果acquired變量大於1,就證明圖片正在被使用,那麼就放到activeResources當中去。經過release()之後,acquired減1,如果減成0了,那就說明當前圖片沒有被正在使用,那就可以調用onResourceReleased()方法來釋放資源。

onResourceReleased()方法中,會將緩存先從activeResources中移除,然後再將它put到LRUResourceCache中。這樣也就實現了正在使用中的圖片使用弱引用來進行緩存,不在使用中的圖片使用LruCache來進行緩存的功能。

4. 硬盤緩存

一樣的,我們首先來看下他的用法。

4.1 用法

Glide.with(this)
     .load(url)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .into(imageView);

而這個diskCacheStrategy()方法基本上就是Glide硬盤緩存的一切,他可以接收四種參數:

  • DiskCacheStrategy.NONE: 表示不緩存任何內容。
  • DiskCacheStrategy.SOURCE: 表示只緩存原始圖片。
  • DiskCacheStrategy.RESULT: 表示只緩存轉換過後的圖片(默認選項)。
  • DiskCacheStrategy.ALL : 表示既緩存原始圖片,也緩存轉換過後的圖片。

4.2 源碼

首先讓我們回到EngineRunnable的run()方法。如果對Glide加載圖片有印象的同學會記得這個方法,這個方法就是在Glide開啓線程加載圖片後執行的。然後run方法裏面又會有一個decode()方法:

private Resource<?> decode() throws Exception {
    if (isDecodingFromCache()) {
        return decodeFromCache();
    } else {
        return decodeFromSource();
    }
}

到這時會分爲兩種情況,一種是調用decodeFromCache()方法從硬盤緩存中讀取圖片,另一種是調用decodeFromSource()來讀取原始圖片,正常情況下會優先從緩存中讀取文件,如果緩存中讀不到再去讀取原始文件。那我們先來看下decodeFromCache()的源碼:

private Resource<?> decodeFromCache() throws Exception {
    Resource<?> result = null;
    try {
        // 調用此方法獲取轉換後的緩存
        result = decodeJob.decodeResultFromCache();
    } catch (Exception e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Exception decoding result from cache: " + e);
        }
    }

    // 調用此方法獲取原始圖片緩存
    if (result == null) {
        result = decodeJob.decodeSourceFromCache();
    }
    return result;
}

這裏調用了兩個獲取緩存的方法,一個是decodeResultFromCache(),另一個是decodeSourceFromCache()方法。decodeResultFromCache()是獲取原始圖片經過Glide轉換後的圖片,而decodeSourceFromCache()則是獲取原始圖片。

那我們來看下這兩個方法:

public Resource<Z> decodeResultFromCache() throws Exception {
    if (!diskCacheStrategy.cacheResult()) {
        return null;
    }

    long startTime = LogTime.getLogTime();
    Resource<T> transformed = loadFromCache(resultKey);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Decoded transformed from cache", startTime);
    }
    startTime = LogTime.getLogTime();
    Resource<Z> result = transcode(transformed);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Transcoded transformed from cache", startTime);
    }
    return result;
}
    
public Resource<Z> decodeSourceFromCache() throws Exception {
    if (!diskCacheStrategy.cacheSource()) {
        return null;
    }

    long startTime = LogTime.getLogTime();
    Resource<T> decoded = loadFromCache(resultKey.getOriginalKey());
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Decoded source from cache", startTime);
    }
    return transformEncodeAndTranscode(decoded);
}

可以看到他兩都是調用了loadFromCache()方法去讀取緩存,但是如果是decodeResultFromCache()就直接將數據返回,如果是decodeSourceFromCache(),則調用transformEncodeAndTranscode()方法將數據轉換一下在返回。

而且還一點就是他兩調用loadFromCache()的參數也不一樣,decodeResultFromCache()方法是直接傳入的key,但是decodeSourceFromCache()傳入的是key.getOriginalKey()

public Key getOriginalKey() {
    if (originalKey == null) {
        originalKey = new OriginalKey(id, signature);
    }
    return originalKey;
}

可以看到,這裏其實就是忽略了絕大部分的參數,只使用了id和signature這兩個參數來構成緩存Key。而signature參數絕大多數情況下都是用不到的,因此基本上可以說就是由id(也就是圖片url)來決定的Original緩存Key。

這塊弄懂了我們就直接來看下loadFromCache()的源碼吧:

private Resource<T> loadFromCache(Key key) throws IOException {
    File cacheFile = diskCacheProvider.getDiskCache().get(key);
    if (cacheFile == null) {
        return null;
    }

    Resource<T> result = null;
    try {
        result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);
    } finally {
        if (result == null) {
            diskCacheProvider.getDiskCache().delete(key);
        }
    }
    return result;
}

先調用diskCacheProvidergetDiskCache()方法獲取到Glide的DiskLruCache工具類的實例,然後調用它的get()方法得到緩存文件。,如果不爲空,就轉碼轉成result並返回。

這樣就是整個磁盤緩存讀取的流程。那他是在哪兒寫入緩存的呢?

我們來看下decodeFromSource這個方法:

public Resource<Z> decodeFromSource() throws Exception {
    // 解析圖片
    Resource<T> decoded = decodeSource();
    // 對圖片進行轉換和轉碼
    return transformEncodeAndTranscode(decoded);
}

我們先來看下decodeSource()這個方法:

private Resource<T> decodeSource() throws Exception {
    Resource<T> decoded = null;
    try {
        long startTime = LogTime.getLogTime();
        // 讀取到數據
        final A data = fetcher.loadData(priority);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Fetched data", startTime);
        }
        if (isCancelled) {
            return null;
        }
        // 調用decodeFromSourceData進行解碼
        decoded = decodeFromSourceData(data);
    } finally {
        fetcher.cleanup();
    }
    return decoded;
}

private Resource<T> decodeFromSourceData(A data) throws IOException {
    final Resource<T> decoded;
    //  判斷是否允許緩存原始圖片
    if (diskCacheStrategy.cacheSource()) {
        // 緩存原始圖片
        decoded = cacheAndDecodeSourceData(data);
    } else {
        long startTime = LogTime.getLogTime();
        // 進行轉碼
        decoded = loadProvider.getSourceDecoder().decode(data, width, height);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Decoded from source", startTime);
        }
    }
    return decoded;
}

private Resource<T> cacheAndDecodeSourceData(A data) throws IOException {
    long startTime = LogTime.getLogTime();
    SourceWriter<A> writer = new SourceWriter<A>(loadProvider.getSourceEncoder(), data);
    diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Wrote source to cache", startTime);
    }

    startTime = LogTime.getLogTime();
    Resource<T> result = loadFromCache(resultKey.getOriginalKey());
    if (Log.isLoggable(TAG, Log.VERBOSE) && result != null) {
        logWithTimeAndKey("Decoded source from cache", startTime);
    }
    return result;
}

首先現在第6行調用fetcher.loadData()來獲取圖片數據,然後再調用decodeFromSourceData()來進行解碼。

decodeFromSourceData()這個方法進去後,先判斷是否允許緩存圖片,如果允許,就調用cacheAndDecodeSourceData()方法。

cacheAndDecodeSourceData()這個方法同樣是通過getDiskCache()方法來獲取DiskLruCache實例,接着調用它的put()方法就可以寫入硬盤緩存了。

這塊就是原始圖片的緩存寫入。接下來我們再來看下transformEncodeAndTranscode()這個方法是如何將轉碼後的圖片寫入緩存的:

private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
    long startTime = LogTime.getLogTime();
    // 對圖片進行轉換
    Resource<T> transformed = transform(decoded);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Transformed resource from source", startTime);
    }

    // 調用此方法將轉換後的結果存入磁盤
    writeTransformedToCache(transformed);

    startTime = LogTime.getLogTime();
    Resource<Z> result = transcode(transformed);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Transcoded transformed from source", startTime);
    }
    return result;
}

private void writeTransformedToCache(Resource<T> transformed) {
    if (transformed == null || !diskCacheStrategy.cacheResult()) {
        return;
    }
    long startTime = LogTime.getLogTime();
    SourceWriter<Resource<T>> writer = new SourceWriter<Resource<T>>(loadProvider.getEncoder(), transformed);
    // 存入LruCache
    diskCacheProvider.getDiskCache().put(resultKey, writer);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Wrote transformed from source to cache", startTime);
    }
}

這裏的邏輯就更加簡單明瞭了。先是在第4行調用transform()方法來對圖片進行轉換,然後在writeTransformedToCache()方法中將轉換過後的圖片寫入到硬盤緩存中,調用的同樣是DiskLruCache實例的put()方法,不過這裏用的緩存Key是resultKey。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章