前言
Android緩存機(jī)制:如果沒有緩存,在大量的網(wǎng)絡(luò)請(qǐng)求從遠(yuǎn)程獲取圖片時(shí)會(huì)造成網(wǎng)絡(luò)流量的浪費(fèi),加載速度較慢,用戶體驗(yàn)不好;
今天我們就來聊聊Glide的緩存機(jī)制
B站100集視頻教程【Android源碼解析Retrofit/OkHttp/Glide/RxJava/EventBus....】:https://www.bilibili.com/video/BV1mT4y1R7F4?spm_id_from=333.999.0.0
一、Glide中緩存概念簡(jiǎn)述

Glide將它分成了兩個(gè)模塊,一個(gè)是內(nèi)存緩存,一個(gè)是硬盤緩存;
1、內(nèi)存緩存
- 內(nèi)存緩存又分為兩級(jí),一級(jí)是LruCache緩存,一級(jí)是弱引用緩存
- 內(nèi)存緩存的作用:防止應(yīng)用重復(fù)將圖片數(shù)據(jù)讀取到內(nèi)存當(dāng)中。
- LruCache緩存:不在使用中的圖片使用LruCache來進(jìn)行緩存。
- 弱引用緩存:把正在使用中的圖片使用弱引用來進(jìn)行緩存,這樣的目的保護(hù)正在使用的資源不會(huì)被LruCache算法回收。
2、硬盤緩存
硬盤緩存的作用:防止應(yīng)用重復(fù)從網(wǎng)絡(luò)或其他地方重復(fù)下載和讀取數(shù)據(jù);
3、圖片請(qǐng)求步驟
開始一個(gè)新的圖片請(qǐng)求之前檢查以下多級(jí)的緩存:
- 內(nèi)存緩存:該圖片是否最近被加載過并仍存在于內(nèi)存中?即LruCache緩存;
- 活動(dòng)資源:現(xiàn)在是否有另一個(gè) View 正在展示這張圖片?也就是弱引用緩存;
- 資源類型:該圖片是否之前曾被解碼、轉(zhuǎn)換并寫入過磁盤緩存?
- 數(shù)據(jù)來源:構(gòu)建這個(gè)圖片的資源是否之前曾被寫入過文件緩存?
- 前兩步檢查圖片是否在內(nèi)存中,如果是則直接返回圖片。后兩步則檢查圖片是否在磁盤上,以便快速但異步地返回圖片;
- 如果四個(gè)步驟都未能找到圖片,則Glide會(huì)返回到原始資源以取回?cái)?shù)據(jù)(原始文件,Uri, Url等);
- 圖片存的順序是:弱引用、內(nèi)存、磁盤;
- 圖片取的順序是:內(nèi)存、弱引用、磁盤;
4、Glide中Bitmap復(fù)用機(jī)制
- Bitmap復(fù)用機(jī)制:將已經(jīng)不需要使用的數(shù)據(jù)空間重新拿來使用,減少內(nèi)存抖動(dòng)(指在短時(shí)間內(nèi)有大量的對(duì)象被創(chuàng)建或者被回收的現(xiàn)象);
- BitmapFactory.Options.inMutable是Glide能夠復(fù)用Bitmap的基石,是BitmapFactory提供的一個(gè)參數(shù),表示該Bitmap是可變的,支持復(fù)用的。BitmapFactory.Options中提供了兩個(gè)屬性:inMutable、inBitmap。當(dāng)進(jìn)行Bitmap復(fù)用時(shí),需要設(shè)置inMutable為true,inBitmap設(shè)置被復(fù)用的已經(jīng)存在的Bitmap。Bitmap復(fù)用池使用LRU算法實(shí)現(xiàn);
二、緩存源碼流程
memory cache和disk cache在Glide創(chuàng)建的時(shí)候也被創(chuàng)建了,Glide創(chuàng)建的代碼在GlideBuilder.build(Context)方法
@NonNull
Glide build(@NonNull Context context) {
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
...);
}
return new Glide(
...
memoryCache,
...);
}
1、內(nèi)存緩存-memoryCache
通過代碼可以看到 memoryCache 被放入 Engine 和 Glide 實(shí)例中。在Engine中利用memoryCache進(jìn)行存取操作,Glide 實(shí)例中的memoryCache是用來在內(nèi)存緊張的時(shí)候,通知memoryCache釋放內(nèi)存。Glide實(shí)現(xiàn)了ComponentCallbacks2接口,在Glide創(chuàng)建完成后,通過applicationContext.registerComponentCallbacks(glide)似的 Glide 實(shí)例可以監(jiān)聽內(nèi)存緊張的信號(hào)。
// Glide
@Override
public void onTrimMemory(int level) {
trimMemory(level);
}
public void trimMemory(int level) {
// Engine asserts this anyway when removing resources, fail faster and consistently
Util.assertMainThread();
// memory cache needs to be trimmed before bitmap pool to trim re-pooled Bitmaps too. See #687.
memoryCache.trimMemory(level);
bitmapPool.trimMemory(level);
arrayPool.trimMemory(level);
}
memoryCache是一個(gè)使用LRU(least recently used)算法實(shí)現(xiàn)的內(nèi)存緩存類LruResourceCache,繼承至LruCache類,并實(shí)現(xiàn)了MemoryCache接口。LruCache定義了LRU算法實(shí)現(xiàn)相關(guān)的操作,而MemoryCache定義的是內(nèi)存緩存相關(guān)的操作。
LruCache 的實(shí)現(xiàn)是利用了 LinkedHashMap 的這種數(shù)據(jù)結(jié)構(gòu)的一個(gè)特性( accessOrder=true 基于訪問順序 )再加上對(duì) LinkedHashMap 的數(shù)據(jù)操作上鎖實(shí)現(xiàn)的緩存策略。
當(dāng)調(diào)用 put()方法時(shí),就會(huì)在集合中添加元素,并調(diào)用
trimToSize()判斷緩存是否已滿,如果滿了就用 LinkedHashMap 的迭代器刪除隊(duì)尾元素,即近期最少訪問的元素。
當(dāng)調(diào)用 get()方法訪問緩存對(duì)象時(shí),就會(huì)調(diào)用 LinkedHashMap 的 get()方法獲得對(duì)應(yīng)集合元素,同時(shí)會(huì)更新該元素到隊(duì)頭
2、磁盤緩存
diskCacheFactory是創(chuàng)建DiskCache的Factory,DiskCache接口定義
public interface DiskCache {
interface Factory {
/** 250 MB of cache. */
int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;
String DEFAULT_DISK_CACHE_DIR = "image_manager_disk_cache";
@Nullable
DiskCache build();
}
interface Writer {
boolean write(@NonNull File file);
}
@Nullable
File get(Key key);
void put(Key key, Writer writer);
@SuppressWarnings("unused")
void delete(Key key);
void clear();
}
接著再來看下DiskCache.Factory的默認(rèn)實(shí)現(xiàn):InternalCacheDiskCacheFactory
public final class InternalCacheDiskCacheFactory extends DiskLruCacheFactory {
public InternalCacheDiskCacheFactory(Context context) {
this(context, DiskCache.Factory.DEFAULT_DISK_CACHE_DIR,
DiskCache.Factory.DEFAULT_DISK_CACHE_SIZE);
}
public InternalCacheDiskCacheFactory(Context context, long diskCacheSize) {
this(context, DiskCache.Factory.DEFAULT_DISK_CACHE_DIR, diskCacheSize);
}
public InternalCacheDiskCacheFactory(final Context context, final String diskCacheName,
long diskCacheSize) {
super(new CacheDirectoryGetter() {
@Override
public File getCacheDirectory() {
File cacheDirectory = context.getCacheDir();
if (cacheDirectory == null) {
return null;
}
if (diskCacheName != null) {
return new File(cacheDirectory, diskCacheName);
}
return cacheDirectory;
}
}, diskCacheSize);
}
}
由以上代碼可以看出:默認(rèn)會(huì)創(chuàng)建一個(gè)250M的緩存目錄,其路徑為/data/data/{package}/cache/image_manager_disk_cache/
繼續(xù)看其父類DiskLruCacheFactory的代碼
public class DiskLruCacheFactory implements DiskCache.Factory {
private final long diskCacheSize;
private final CacheDirectoryGetter cacheDirectoryGetter;
public interface CacheDirectoryGetter {
File getCacheDirectory();
}
...
public DiskLruCacheFactory(CacheDirectoryGetter cacheDirectoryGetter, long diskCacheSize) {
this.diskCacheSize = diskCacheSize;
this.cacheDirectoryGetter = cacheDirectoryGetter;
}
@Override
public DiskCache build() {
File cacheDir = cacheDirectoryGetter.getCacheDirectory();
if (cacheDir == null) {
return null;
}
if (!cacheDir.mkdirs() && (!cacheDir.exists() || !cacheDir.isDirectory())) {
return null;
}
return DiskLruCacheWrapper.create(cacheDir, diskCacheSize);
}
}
DiskLruCacheFactory.build()方法會(huì)返回一個(gè)DiskLruCacheWrapper類的實(shí)例,看下DiskLruCacheWrapper的實(shí)現(xiàn)
public class DiskLruCacheWrapper implements DiskCache {
private static final String TAG = "DiskLruCacheWrapper";
private static final int APP_VERSION = 1;
private static final int VALUE_COUNT = 1;
private static DiskLruCacheWrapper wrapper;
private final SafeKeyGenerator safeKeyGenerator;
private final File directory;
private final long maxSize;
private final DiskCacheWriteLocker writeLocker = new DiskCacheWriteLocker();
private DiskLruCache diskLruCache;
@SuppressWarnings("deprecation")
public static DiskCache create(File directory, long maxSize) {
return new DiskLruCacheWrapper(directory, maxSize);
}
@Deprecated
@SuppressWarnings({"WeakerAccess", "DeprecatedIsStillUsed"})
protected DiskLruCacheWrapper(File directory, long maxSize) {
this.directory = directory;
this.maxSize = maxSize;
this.safeKeyGenerator = new SafeKeyGenerator();
}
private synchronized DiskLruCache getDiskCache() throws IOException {
if (diskLruCache == null) {
diskLruCache = DiskLruCache.open(directory, APP_VERSION, VALUE_COUNT, maxSize);
}
return diskLruCache;
}
@Override
public File get(Key key) {
String safeKey = safeKeyGenerator.getSafeKey(key);
File result = null;
try {
final DiskLruCache.Value value = getDiskCache().get(safeKey);
if (value != null) {
result = value.getFile(0);
}
} catch (IOException e) {
...
}
return result;
}
@Override
public void put(Key key, Writer writer) {
String safeKey = safeKeyGenerator.getSafeKey(key);
writeLocker.acquire(safeKey);
try {
try {
DiskLruCache diskCache = getDiskCache();
Value current = diskCache.get(safeKey);
...
DiskLruCache.Editor editor = diskCache.edit(safeKey);
...
try {
File file = editor.getFile(0);
if (writer.write(file)) {
editor.commit();
}
} finally {
editor.abortUnlessCommitted();
}
} catch (IOException e) {
...
}
} finally {
writeLocker.release(safeKey);
}
}
...
}
里面包裝了一個(gè)DiskLruCache,該類主要是為DiskLruCache提供了一個(gè)根據(jù)Key生成safeKey的SafeKeyGenerator以及寫鎖DiskCacheWriteLocker。
回到GlideBuilder.build(Context)中,diskCacheFactory會(huì)被傳進(jìn)Engine中,在Engine的構(gòu)造方法中會(huì)被包裝成為一個(gè)LazyDiskCacheProvider,在被需要的時(shí)候調(diào)用getDiskCache()方法,這樣就會(huì)調(diào)用factory的build()方法返回一個(gè)DiskCache。代碼如下:
private static class LazyDiskCacheProvider implements DecodeJob.DiskCacheProvider {
private final DiskCache.Factory factory;
private volatile DiskCache diskCache;
LazyDiskCacheProvider(DiskCache.Factory factory) {
this.factory = factory;
}
...
@Override
public DiskCache getDiskCache() {
if (diskCache == null) {
synchronized (this) {
if (diskCache == null) {
diskCache = factory.build();
}
if (diskCache == null) {
diskCache = new DiskCacheAdapter();
}
}
}
return diskCache;
}
}
LazyDiskCacheProvider會(huì)在Engine后面的初始化流程中作為入?yún)鞯紻ecodeJobFactory的構(gòu)造器。在DecodeJobFactory創(chuàng)建DecodeJob時(shí)也會(huì)作為入?yún)?huì)傳進(jìn)去,DecodeJob中會(huì)以全局變量保存此LazyDiskCacheProvider,在資源加載完畢并展示后,會(huì)進(jìn)行緩存的存儲(chǔ)。同時(shí),DecodeJob也會(huì)在DecodeHelper初始化時(shí),將此DiskCacheProvider設(shè)置進(jìn)去,供ResourceCacheGenerator、DataCacheGenerator讀取緩存,供SourceGenerator寫入緩存
3、 ActiveResources
ActiveResources在Engine的構(gòu)造器中被創(chuàng)建,在ActiveResources的構(gòu)造器中會(huì)啟動(dòng)一個(gè)后臺(tái)優(yōu)先級(jí)級(jí)別(THREAD_PRIORITY_BACKGROUND)的線程,在該線程中會(huì)調(diào)用cleanReferenceQueue()方法一直循環(huán)清除ReferenceQueue中的將要被GC的Resource。
final class ActiveResources {
private final boolean isActiveResourceRetentionAllowed;
private final Executor monitorClearedResourcesExecutor;
@VisibleForTesting
final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();
private volatile boolean isShutdown;
ActiveResources(boolean isActiveResourceRetentionAllowed) {
this(
isActiveResourceRetentionAllowed,
java.util.concurrent.Executors.newSingleThreadExecutor(
new ThreadFactory() {
@Override
public Thread newThread(@NonNull final Runnable r) {
return new Thread(
new Runnable() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
r.run();
}
},
"glide-active-resources");
}
}));
}
@VisibleForTesting
ActiveResources(
boolean isActiveResourceRetentionAllowed, Executor monitorClearedResourcesExecutor) {
this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
this.monitorClearedResourcesExecutor = monitorClearedResourcesExecutor;
monitorClearedResourcesExecutor.execute(
new Runnable() {
@Override
public void run() {
cleanReferenceQueue();
}
});
}
@SuppressWarnings("WeakerAccess")
@Synthetic void cleanReferenceQueue() {
while (!isShutdown) {
try {
ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
cleanupActiveReference(ref);
// This section for testing only.
DequeuedResourceCallback current = cb;
if (current != null) {
current.onResourceDequeued();
}
// End for testing only.
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
先來看看ActiveResources的activate方法(保存)、deactivate方法(刪除)的方法
synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
synchronized void deactivate(Key key) {
ResourceWeakReference removed = activeEngineResources.remove(key);
if (removed != null) {
removed.reset();
}
}
activate方法會(huì)將參數(shù)封裝成為一個(gè)ResourceWeakReference,然后放入map中,如果對(duì)應(yīng)的key之前有值,那么調(diào)用之前值的reset方法進(jìn)行清除。deactivate方法先在map中移除,然后調(diào)用resource的reset方法進(jìn)行清除。ResourceWeakReference繼承WeakReference,內(nèi)部只是保存了Resource的一些屬性。
static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
@SuppressWarnings("WeakerAccess") @Synthetic final Key key;
@SuppressWarnings("WeakerAccess") @Synthetic final boolean isCacheable;
@Nullable @SuppressWarnings("WeakerAccess") @Synthetic Resource<?> resource;
@Synthetic
@SuppressWarnings("WeakerAccess")
ResourceWeakReference(
@NonNull Key key,
@NonNull EngineResource<?> referent,
@NonNull ReferenceQueue<? super EngineResource<?>> queue,
boolean isActiveResourceRetentionAllowed) {
super(referent, queue);
this.key = Preconditions.checkNotNull(key);
this.resource =
referent.isCacheable() && isActiveResourceRetentionAllowed
? Preconditions.checkNotNull(referent.getResource()) : null;
isCacheable = referent.isCacheable();
}
}
構(gòu)造方法中調(diào)用了super(referent, queue),這樣做可以讓將要被GC的對(duì)象放入到ReferenceQueue中。而ActiveResources.cleanReferenceQueue()方法會(huì)一直嘗試從queue中獲取將要被GC的resource,然后調(diào)用cleanupActiveReference方法將resource從activeEngineResources中移除。cleanupActiveReference源碼如下:
void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
synchronized (listener) {
synchronized (this) {
// 移除active資源
activeEngineResources.remove(ref.key);
if (!ref.isCacheable || ref.resource == null) {
return;
}
// 構(gòu)造新的 Resource
EngineResource<?> newResource =
new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false);
newResource.setResourceListener(ref.key, listener);
// 回調(diào)Engine的onResourceReleased方法
// 這會(huì)導(dǎo)致此資源從active變成memory cache狀態(tài)
listener.onResourceReleased(ref.key, newResource);
}
}
}
Engine實(shí)現(xiàn)了EngineResource.ResourceListener,此處的listener就是Engine,最終會(huì)回調(diào)Engine.onResourceReleased
@Override
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
如果資源可以被緩存,則緩存到 memory cache,否則對(duì)資源進(jìn)行回收
4、磁盤緩存讀取
我們分析下緩存的存取代碼。我們看下
public synchronized <R> LoadStatus load(...) {
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
return null;
}
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null;
}
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(...);
DecodeJob<R> decodeJob =
decodeJobFactory.build(...);
jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
緩存需要根據(jù)EngineKey去存取,先看下EngineKey的構(gòu)造方法
EngineKey(
Object model,
Key signature,
int width
int height,
Map<Class<?>, Transformation<?>> transformations,
Class<?> resourceClass,
Class<?> transcodeClass,
Options options)
- model:load方法傳的參數(shù);
- signature:BaseRequestOptions的成員變量,默認(rèn)會(huì)是EmptySignature.obtain()
- 在加載本地resource資源時(shí)會(huì)變成ApplicationVersionSignature.obtain(context);
- width、height:如果沒有指定override(int size),那么將得到view的size;
- transformations:默認(rèn)會(huì)基于ImageView的scaleType設(shè)置對(duì)應(yīng)的四個(gè)Transformation;
- 如果指定了transform,那么就基于該值進(jìn)行設(shè)置;
- resourceClass:解碼后的資源,如果沒有asBitmap、asGif,一般會(huì)是Object;
- transcodeClass:最終要轉(zhuǎn)換成的數(shù)據(jù)類型,根據(jù)as方法確定,加載本地res或者網(wǎng)絡(luò)URL,都會(huì)調(diào)用asDrawable,所以為Drawable
- options:如果沒有設(shè)置過transform,此處會(huì)根據(jù)ImageView的scaleType默認(rèn)指定一個(gè)option;
- 所以,在多次加載同一個(gè)model的過程中,只要上述任何一個(gè)參數(shù)有改變,都不會(huì)認(rèn)為是同一個(gè)key;
回到Engine.load方法,從緩存加載成功后的回調(diào)cb.onResourceReady(cached, DataSource.MEMORY_CACHE);可以看到:active狀態(tài)的資源和memory cache狀態(tài)的資源都是DataSource.MEMORY_CACHE,并且加載的資源都是 EngineResource 對(duì)象,該對(duì)象內(nèi)部采用了引用計(jì)數(shù)去判斷資源是否被釋放,如果引用計(jì)數(shù)為0,那么會(huì)調(diào)用listener.onResourceReleased(key, this)方法通知外界此資源已經(jīng)釋放了。這里的listener是ResourceListener類型的接口,只有一個(gè)onResourceReleased(Key key, EngineResource<?> resource)方法,Engine實(shí)現(xiàn)了該接口,此處的listener就是Engine。在Engine.onResourceReleased方法中會(huì)判斷資源是否可緩存,可緩存則將此資源放入memory cache中,否則回收掉該資源,代碼如下:
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
// 從activeResources中移除
activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
// 存入 MemoryCache
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
繼續(xù)回到Engine.load方法,先來看下active資源獲取的方法
@Nullable
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
// 設(shè)置skipMemoryCache(true),則isMemoryCacheable為false,跳過ActiveResources
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = activeResources.get(key);
if (active != null) {
// 命中緩存,引用計(jì)數(shù)+1
active.acquire();
}
return active;
}
繼續(xù)分析cached資源獲取的方法,如果從active資源中沒有獲取到緩存,則繼續(xù)從內(nèi)存緩存中查找
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
// 設(shè)置skipMemoryCache(true),則isMemoryCacheable為false,跳過ActiveResources
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
// 命中緩存,引用計(jì)數(shù)+1
cached.acquire();
// 將此資源從memoryCache中移到activeResources中
activeResources.activate(key, cached);
}
return cached;
}
如果從memoryCache中獲取到資源則將此資源從memoryCache中移到activeResources中。第一次加載的時(shí)候activeResources和memoryCache中都沒有緩存的,后面繼續(xù)通過DecodeJob和EngineJob去加載資源。DecoceJob實(shí)現(xiàn)了Runnable接口,然后會(huì)被EngineJob.start方法提交到對(duì)應(yīng)的線程池中去執(zhí)行。在DecoceJob的run方法中,會(huì)依次從ResourceCacheGenerator和DataCacheGenerator中去取緩存數(shù)據(jù),當(dāng)這兩者都取不到的情況下,會(huì)交給SourceGenerator加載網(wǎng)絡(luò)圖片或者本地資源。resource資源和data資源都是磁盤緩存中的資源。
先看下 ResourceCacheGenerator.startNext
@Override
public boolean startNext() {
// list里面只有一個(gè)GlideUrl對(duì)象
List<Key> sourceIds = helper.getCacheKeys();
if (sourceIds.isEmpty()) {
return false;
}
// 獲得了三個(gè)可以到達(dá)的registeredResourceClasses
// GifDrawable、Bitmap、BitmapDrawable
List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
if (resourceClasses.isEmpty()) {
if (File.class.equals(helper.getTranscodeClass())) {
return false;
}
throw new IllegalStateException(
"Failed to find any load path from " + helper.getModelClass() + " to "
+ helper.getTranscodeClass());
}
// 遍歷sourceIds中的每一個(gè)key、resourceClasses中每一個(gè)class,以及其他的一些值組成key
// 嘗試在磁盤緩存中以key找到緩存文件
while (modelLoaders == null || !hasNextModelLoader()) {
resourceClassIndex++;
if (resourceClassIndex >= resourceClasses.size()) {
sourceIdIndex++;
if (sourceIdIndex >= sourceIds.size()) {
return false;
}
resourceClassIndex = 0;
}
Key sourceId = sourceIds.get(sourceIdIndex);
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = helper.getTransformation(resourceClass);
// PMD.AvoidInstantiatingObjectsInLoops Each iteration is comparatively expensive anyway,
// we only run until the first one succeeds, the loop runs for only a limited
// number of iterations on the order of 10-20 in the worst case.
// 構(gòu)造key
currentKey =
new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
// 查找緩存文件
cacheFile = helper.getDiskCache().get(currentKey);
// 如果找到了緩存文件,循環(huán)條件則會(huì)為false,退出循環(huán)
if (cacheFile != null) {
sourceKey = sourceId;
// 1\. 找出注入時(shí)以File.class為modelClass的注入代碼
// 2\. 調(diào)用所有注入的factory.build方法得到ModelLoader
// 3 .過濾掉不可能處理model的ModelLoader
// 此時(shí)的modelLoaders值為:
// [ByteBufferFileLoader, FileLoader, FileLoader, UnitModelLoader]
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
// 如果找到了緩存文件,hasNextModelLoader()方法則會(huì)為true,可以執(zhí)行循環(huán)
// 沒有找到緩存文件,則不會(huì)進(jìn)入循環(huán),會(huì)直接返回false
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
// 在循環(huán)中會(huì)依次判斷某個(gè)ModelLoader能不能加載此文件
loadData = modelLoader.buildLoadData(cacheFile,
helper.getWidth(), helper.getHeight(), helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
// 如果某個(gè)ModelLoader可以,那么就調(diào)用其fetcher進(jìn)行加載數(shù)據(jù)
// 加載成功或失敗會(huì)通知自身
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
該方法的相關(guān)注釋代碼里都有標(biāo)明。找緩存時(shí)key的類型為ResourceCacheKey,我們先來看下ResourceCacheKey的構(gòu)成
currentKey =
new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
ResourceCacheKey(
ArrayPool arrayPool,
Key sourceKey,
Key signature,
int width,
int height,
Transformation<?> appliedTransformation,
Class<?> decodedResourceClass,
Options options)
- arrayPool:默認(rèn)值是LruArrayPool,不參與key的equals方法;
- sourceKey:如果請(qǐng)求的是URL,此處就是GlideUrl(GlideUrl implements Key);
- signature:BaseRequestOptions的成員變量,默認(rèn)會(huì)是EmptySignature.obtain(),
- 在加載本地resource資源時(shí)會(huì)變成ApplicationVersionSignature.obtain(context);
- width、height:如果沒有指定override(int size),那么將得到view的size;
- appliedTransformation:默認(rèn)會(huì)根據(jù)ImageView的scaleType設(shè)置對(duì)應(yīng)的BitmapTransformation;
- 如果指定了transform,那么就會(huì)是指定的值;
- decodedResourceClass:可以被編碼成的資源類型,如BitmapDrawable等;
- options:如果沒有設(shè)置過transform,此處會(huì)根據(jù)ImageView的scaleType默認(rèn)指定一個(gè)option;
在ResourceCacheKey中,arrayPool并沒有參與equals方法;
生成ResourceCacheKey之后會(huì)根據(jù)key去磁盤緩存中查找cacheFile = helper.getDiskCache().get(currentKey);
helper.getDiskCache()返回DiskCache接口,它的實(shí)現(xiàn)類是DiskLruCacheWrapper,看下DiskLruCacheWrapper.get方法
@Override
public File get(Key key) {
String safeKey = safeKeyGenerator.getSafeKey(key);
...
File result = null;
try {
final DiskLruCache.Value value = getDiskCache().get(safeKey);
if (value != null) {
result = value.getFile(0);
}
} catch (IOException e) {
...
}
return result;
}
這里調(diào)用SafeKeyGenerator生成了一個(gè)String類型的SafeKey,實(shí)際上就是對(duì)原始key中每個(gè)字段都使用SHA-256加密,然后將得到的字節(jié)數(shù)組轉(zhuǎn)換為16進(jìn)制的字符串。生成SafeKey后,接著根據(jù)SafeKey去DiskCache里面找對(duì)應(yīng)的緩存文件,然后返回文件。
回到ResourceCacheGenerator.startNext方法中,如果找到了緩存會(huì)調(diào)用loadData.fetcher.loadData(helper.getPriority(), this);這里的 fetcher 是 ByteBufferFetcher,ByteBufferFetcher的loadData方法中最終會(huì)執(zhí)行callback.onDataReady(result)這里callback是ResourceCacheGenerator
public void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE,
currentKey);
}
ResourceCacheGenerator的onDataReady方法又會(huì)回調(diào)DecodeJob的onDataFetcherReady方法進(jìn)行后續(xù)的解碼操作。
如果ResourceCacheGenerator沒有找到緩存,就會(huì)交給DataCacheGenerator繼續(xù)查找緩存。該類大體流程和ResourceCacheGenerator一樣,有點(diǎn)不同的是,DataCacheGenerator的構(gòu)造器有兩個(gè)構(gòu)造器,其中的DataCacheGenerator(List<key style="box-sizing: border-box;">, DecodeHelper<?>, FetcherReadyCallback)構(gòu)造器是給SourceGenerator準(zhǔn)備的。因?yàn)槿绻麤]有磁盤緩存,那么從源頭加載后,肯定需要進(jìn)行磁盤緩存操作的。所以,SourceGenerator會(huì)將加載后的資源保存到磁盤中,然后轉(zhuǎn)交給DataCacheGenerator從磁盤中取出交給ImageView展示。</key>
看下DataCacheGenerator.startNext
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
Key sourceId = cacheKeys.get(sourceIdIndex);
...
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
...
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData =
modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
這里的originalKey是DataCacheKey類型的,DataCacheKey構(gòu)造方法如下
DataCacheKey(Key sourceKey, Key signature)
這里的sourceKey和signature與ResourceCacheKey中的兩個(gè)變量一致,從這里就可以看出:DataCache緩存的是原始的數(shù)據(jù),ResourceCache緩存的是是被解碼、轉(zhuǎn)換后的數(shù)據(jù)。
如果DataCacheGenerator沒有取到緩存,那么會(huì)交給SourceGenerator從源頭加載??聪耂ourceGenerator的startNext方法
@Override
public boolean startNext() {
// 首次運(yùn)行dataToCache為null
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
// 首次運(yùn)行sourceCacheGenerator為null
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
加載成功后依然會(huì)回調(diào)SourceGenerator的onDataReady方法
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// cb 為 DecodeJob
cb.reschedule();
} else {
// cb 為 DecodeJob
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
先判斷獲取到的數(shù)據(jù)是否需要進(jìn)行磁盤緩存,如果需要磁盤緩存,則經(jīng)過DecodeJob、EngineJob的調(diào)度,重新調(diào)用SourceGenerator.startNext方法,此時(shí)dataToCache已經(jīng)被賦值,則會(huì)調(diào)用cacheData(data);進(jìn)行磁盤緩存的寫入,并轉(zhuǎn)交給DataCacheGenerator完成后續(xù)的處理;否則就通知DecodeJob已經(jīng)加載成功。
先看下SourceGenerator的startNext方法中調(diào)用的SourceGenerator.cacheData(data)
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
DataCacheWriter<Object> writer =
new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
helper.getDiskCache().put(originalKey, writer);
...
} finally {
loadData.fetcher.cleanup();
}
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
cacheData方法先構(gòu)建了一個(gè)DataCacheKey將data寫入了磁盤,然后new了一個(gè)DataCacheGenerator賦值給sourceCacheGenerator?;氐絪tartNext繼續(xù)向下執(zhí)行,此時(shí)sourceCacheGenerator不為空,就調(diào)用其startNext()方法從磁盤中加載剛寫入磁盤的數(shù)據(jù),并返回true讓DecodeJob停止嘗試獲取數(shù)據(jù)。此時(shí),從磁盤緩存中讀取數(shù)據(jù)的邏輯已經(jīng)完成,接下來是寫磁盤緩存。
假如SourceGenerator的onDataReady方法中的磁盤緩存策略不可用,則會(huì)回調(diào)DecodeJob.onDataFetcherReady方法
// DecodeJob
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;
this.currentData = data;
this.currentFetcher = fetcher;
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
private void decodeFromRetrievedData() {
...
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
decodeFromRetrievedData();后續(xù)的方法調(diào)用鏈在之前的文章中分析過,主要做的事情就是:將原始的data數(shù)據(jù)轉(zhuǎn)變?yōu)榭梢怨㊣mageView顯示的resource數(shù)據(jù)并將其顯示在ImageView上。
將原始的data數(shù)據(jù)轉(zhuǎn)變?yōu)閞esource數(shù)據(jù)后,會(huì)調(diào)用DecodeJob.onResourceDecoded(dataSource, decoded)
@Synthetic
@NonNull
<Z> Resource<Z> onResourceDecoded(DataSource dataSource,
@NonNull Resource<Z> decoded) {
@SuppressWarnings("unchecked")
Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
Transformation<Z> appliedTransformation = null;
Resource<Z> transformed = decoded;
// 不是 resource cache時(shí)要transform
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
transformed = appliedTransformation.transform(glideContext, decoded, width, height);
}
// TODO: Make this the responsibility of the Transformation.
if (!decoded.equals(transformed)) {
decoded.recycle();
}
final EncodeStrategy encodeStrategy;
final ResourceEncoder<Z> encoder;
if (decodeHelper.isResourceEncoderAvailable(transformed)) {
encoder = decodeHelper.getResultEncoder(transformed);
encodeStrategy = encoder.getEncodeStrategy(options);
} else {
encoder = null;
encodeStrategy = EncodeStrategy.NONE;
}
Resource<Z> result = transformed;
boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
encodeStrategy)) {
if (encoder == null) {
throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
}
final Key key;
switch (encodeStrategy) {
case SOURCE:
key = new DataCacheKey(currentSourceKey, signature);
break;
case TRANSFORMED:
key =
new ResourceCacheKey(
decodeHelper.getArrayPool(),
currentSourceKey,
signature,
width,
height,
appliedTransformation,
resourceSubClass,
options);
break;
default:
throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
}
LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
deferredEncodeManager.init(key, encoder, lockedResult);
result = lockedResult;
}
return result;
}
然后是此過程中的磁盤緩存過程,影響的因素有encodeStrategy、DiskCacheStrategy.isResourceCacheable。encodeStrategy根據(jù)resource數(shù)據(jù)的類型來判斷,如果是Bitmap或BitmapDrawable,那么就是TRANSFORMED;如果是GifDrawable,那么就是SOURCE。磁盤緩存策略默認(rèn)是DiskCacheStrategy.AUTOMATIC。源碼如下:
public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() {
public boolean isDataCacheable(DataSource dataSource) {
return dataSource == DataSource.REMOTE;
}
public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
return (isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE || dataSource == DataSource.LOCAL) && encodeStrategy == EncodeStrategy.TRANSFORMED;
}
public boolean decodeCachedResource() {
return true;
}
public boolean decodeCachedData() {
return true;
}
};
- 只有dataSource為DataSource.LOCAL且encodeStrategy為EncodeStrategy.TRANSFORMED時(shí),才允許緩存。也就是只有本地的resource數(shù)據(jù)為Bitmap或BitmapDrawable的資源才可以緩存。
- 在DecodeJob.onResourceDecoded中會(huì)調(diào)用deferredEncodeManager.init(key, encoder, lockedResult);去初始化deferredEncodeManager。
在DecodeJob的decodeFromRetrievedData();中拿到resource數(shù)據(jù)后會(huì)調(diào)用notifyEncodeAndRelease(resource, currentDataSource)利用deferredEncodeManager對(duì)象進(jìn)行磁盤緩存的寫入;
otifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
...
// 通知回調(diào),資源已經(jīng)就緒
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
}
onEncodeComplete();
}
deferredEncodeManager.encode行磁盤緩存的寫入
// DecodeJob
private static class DeferredEncodeManager<Z> {
private Key key;
private ResourceEncoder<Z> encoder;
private LockedResource<Z> toEncode;
@Synthetic
DeferredEncodeManager() { }
// We just need the encoder and resource type to match, which this will enforce.
@SuppressWarnings("unchecked")
<X> void init(Key key, ResourceEncoder<X> encoder, LockedResource<X> toEncode) {
this.key = key;
this.encoder = (ResourceEncoder<Z>) encoder;
this.toEncode = (LockedResource<Z>) toEncode;
}
void encode(DiskCacheProvider diskCacheProvider, Options options) {
GlideTrace.beginSection("DecodeJob.encode");
try {
// 存入磁盤緩存
diskCacheProvider.getDiskCache().put(key,
new DataCacheWriter<>(encoder, toEncode, options));
} finally {
toEncode.unlock();
GlideTrace.endSection();
}
}
boolean hasResourceToEncode() {
return toEncode != null;
}
void clear() {
key = null;
encoder = null;
toEncode = null;
}
}
diskCacheProvider.getDiskCache()獲取到DiskLruCacheWrapper,并調(diào)用DiskLruCacheWrapper的put寫入。DiskLruCacheWrapper在寫入的時(shí)候會(huì)使用到寫鎖DiskCacheWriteLocker,鎖對(duì)象由對(duì)象池WriteLockPool創(chuàng)建,寫鎖WriteLock實(shí)現(xiàn)是一個(gè)不公平鎖ReentrantLock。
在緩存寫入前,會(huì)判斷key對(duì)應(yīng)的value存不存在,若存在則不寫入。緩存的真正寫入會(huì)由DataCacheWriter交給ByteBufferEncoder和StreamEncoder兩個(gè)具體類來寫入,前者負(fù)責(zé)將ByteBuffer寫入到文件,后者負(fù)責(zé)將InputStream寫入到文件。
目前為止,磁盤緩存的讀寫流程都已分析完成;
5、內(nèi)存緩存:ActiveResource與MemoryCache讀取
回到DecodeJob.notifyEncodeAndRelease方法中,經(jīng)過notifyComplete、EngineJob.onResourceReady、notifyCallbacksOfResult方法中。
在該方法中一方面會(huì)將原始的resource包裝成一個(gè)EngineResource,然后通過回調(diào)傳給Engine.onEngineJobComplete
@Override
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
// 設(shè)置資源的回調(diào)為自己,這樣在資源釋放時(shí)會(huì)通知自己的回調(diào)方法
if (resource != null) {
resource.setResourceListener(key, this);
// 將資源放入activeResources中,資源變?yōu)閍ctive狀態(tài)
if (resource.isCacheable()) {
activeResources.activate(key, resource);
}
}
// 將engineJob從Jobs中移除
jobs.removeIfCurrent(key, engineJob);
}
在這里會(huì)將資源放入activeResources中,資源變?yōu)閍ctive狀態(tài)。后面會(huì)使用Executors.mainThreadExecutor()調(diào)用SingleRequest.onResourceReady回調(diào)進(jìn)行資源的顯示。在觸發(fā)回調(diào)前后各有一個(gè)地方會(huì)對(duì)engineResource進(jìn)行acquire()和release()操作,這兩個(gè)操作分別發(fā)生在notifyCallbacksOfResult()方法的incrementPendingCallbacks、decrementPendingCallbacks()調(diào)用中
@Synthetic
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this) {
...
engineResource = engineResourceFactory.build(resource, isCacheable);
...
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
listener.onEngineJobComplete(this, localKey, localResource);
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
decrementPendingCallbacks();
}
synchronized void incrementPendingCallbacks(int count) {
...
if (pendingCallbacks.getAndAdd(count) == 0 && engineResource != null) {
engineResource.acquire();
}
}
synchronized void decrementPendingCallbacks() {
...
int decremented = pendingCallbacks.decrementAndGet();
if (decremented == 0) {
if (engineResource != null) {
engineResource.release();
}
release();
}
}
private class CallResourceReady implements Runnable {
private final ResourceCallback cb;
CallResourceReady(ResourceCallback cb) {
this.cb = cb;
}
@Override
public void run() {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
// Acquire for this particular callback.
engineResource.acquire();
callCallbackOnResourceReady(cb);
removeCallback(cb);
}
decrementPendingCallbacks();
}
}
}
CallResourceReady的run方法中也會(huì)調(diào)用engineResource.acquire(),上面的代碼調(diào)用結(jié)束后,engineResource的引用計(jì)數(shù)為1。engineResource的引用計(jì)數(shù)會(huì)在RequestManager.onDestory方法中最終調(diào)用SingleRequest.clear()方法,SingleRequest.clear()內(nèi)部調(diào)用releaseResource()、Engine.release 進(jìn)行釋放,這樣引用計(jì)數(shù)就變?yōu)?。引用計(jì)數(shù)就變?yōu)?后會(huì)通知Engine將此資源從active狀態(tài)變成memory cache狀態(tài)。如果我們?cè)俅渭虞d資源時(shí)可以從memory cache中加載,那么資源又會(huì)從memory cache狀態(tài)變成active狀態(tài)。也就是說,在資源第一次顯示后,我們關(guān)閉頁(yè)面,資源會(huì)由active變成memory cache;然后我們?cè)俅芜M(jìn)入頁(yè)面,加載時(shí)會(huì)命中memory cache,從而又變成active狀態(tài)
總結(jié)
- 讀取內(nèi)存緩存時(shí),先從LruCache算法機(jī)制的內(nèi)存緩存讀取,再?gòu)娜跻脵C(jī)制的內(nèi)存緩存讀?。?/li>
- 寫入內(nèi)存緩存時(shí),先寫入 弱引用機(jī)制 的內(nèi)存緩存,等到圖片不再被使用時(shí),再寫入到 LruCache算法機(jī)制的內(nèi)存緩存;
- 讀取磁盤緩存時(shí),先讀取轉(zhuǎn)換后圖片的緩存,再讀取原始圖片的緩存;
B站100集視頻教程【Android源碼解析Retrofit/OkHttp/Glide/RxJava/EventBus....】:https://www.bilibili.com/video/BV1mT4y1R7F4?spm_id_from=333.999.0.0