通過(guò)前面的分析利朵,我們知道真正去加載數(shù)據(jù)是在SingleRequest#onSizeReady方法中被觸發(fā)律想,這個(gè)里面是調(diào)用了Engine#load方法,看到這個(gè)方法绍弟,我們大致可以猜到此時(shí)便開(kāi)始去真正加載數(shù)據(jù)了技即,從緩存中讀取或者是從網(wǎng)絡(luò)獲取等等。在開(kāi)始之前樟遣,我們先簡(jiǎn)單了解一下Engine類中涉及到的一些類而叼。
仍然以最簡(jiǎn)單的load方式為例子
Glide.with(this)
.load("https://p.upyun.com/docs/cloud/demo.jpg")
.into(imageView);
關(guān)鍵類
Key
唯一標(biāo)識(shí)一些數(shù)據(jù)的接口。詳細(xì)介紹->Key結(jié)構(gòu)EngineKey
實(shí)現(xiàn)了Key接口豹悬,用做多路復(fù)用負(fù)載的內(nèi)存緩存鍵Resource
一個(gè)包裝了特定類型的資源接口葵陵,并且能夠匯集和重用。詳細(xì)介紹->Resource結(jié)構(gòu)MemoryCache
內(nèi)存緩存接口屿衅,用于在內(nèi)存緩存中添加和移除資源埃难,這里的實(shí)現(xiàn)類是LruResourceCache,繼承了LruCache涤久。存放的是Key和Resource鍵值對(duì)涡尘。詳細(xì)介紹->MemoryCache結(jié)構(gòu)DiskCache
這里的DiskCache是由InternalCacheDiskCacheFactory創(chuàng)建,其繼承自DiskLruCacheFactory响迂,最終DiskCache的實(shí)現(xiàn)類是DiskLruCacheWrapper對(duì)象考抄。詳細(xì)介紹->DiskCache結(jié)構(gòu)ActiveResources
存放了已經(jīng)被Request請(qǐng)求的資源,是內(nèi)存緩存的一種蔗彤。ResourceRecycler
一個(gè)回收Resource的輔助類川梅,防止陷入遞歸疯兼,當(dāng)回收的Resource資源有子資源的時(shí)候。EngineJob
通過(guò)添加和刪除回調(diào)以進(jìn)行加載并在加載完成時(shí)通知回調(diào)來(lái)管理加載的類DecodeJob
負(fù)責(zé)從緩存數(shù)據(jù)或原始資源解碼資源并應(yīng)用轉(zhuǎn)換和轉(zhuǎn)碼的類贫途。Jobs
一個(gè)負(fù)責(zé)緩存EngineJob的管理類吧彪,里面存放了Key與EngineJob的Map對(duì)象。
有了上面的幾把認(rèn)知之后丢早,我們來(lái)看看代碼的實(shí)現(xiàn)姨裸,先分析Engine的構(gòu)造方法,如果覺(jué)得思路很亂怨酝,建議先看本文結(jié)尾的總結(jié)傀缩,宏觀有一個(gè)大致的了解,再來(lái)看這些過(guò)程农猬。
1. Engine#Engine
public Engine(
MemoryCache memoryCache,
DiskCache.Factory diskCacheFactory,
GlideExecutor diskCacheExecutor,
GlideExecutor sourceExecutor,
GlideExecutor sourceUnlimitedExecutor,
GlideExecutor animationExecutor,
boolean isActiveResourceRetentionAllowed) {
this(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
sourceUnlimitedExecutor,
animationExecutor,
/*jobs=*/ null,
/*keyFactory=*/ null,
/*activeResources=*/ null,
/*engineJobFactory=*/ null,
/*decodeJobFactory=*/ null,
/*resourceRecycler=*/ null,
isActiveResourceRetentionAllowed);
}
@VisibleForTesting
Engine(MemoryCache cache,
DiskCache.Factory diskCacheFactory,
GlideExecutor diskCacheExecutor,
GlideExecutor sourceExecutor,
GlideExecutor sourceUnlimitedExecutor,
GlideExecutor animationExecutor,
Jobs jobs,
EngineKeyFactory keyFactory,
ActiveResources activeResources,
EngineJobFactory engineJobFactory,
DecodeJobFactory decodeJobFactory,
ResourceRecycler resourceRecycler,
boolean isActiveResourceRetentionAllowed) {
this.cache = cache;
this.diskCacheProvider = new LazyDiskCacheProvider(diskCacheFactory);
if (activeResources == null) {
activeResources = new ActiveResources(isActiveResourceRetentionAllowed);
}
this.activeResources = activeResources;
activeResources.setListener(this);
if (keyFactory == null) {
keyFactory = new EngineKeyFactory();
}
this.keyFactory = keyFactory;
if (jobs == null) {
jobs = new Jobs();
}
this.jobs = jobs;
if (engineJobFactory == null) {
engineJobFactory =
new EngineJobFactory(
diskCacheExecutor, sourceExecutor, sourceUnlimitedExecutor, animationExecutor, this);
}
this.engineJobFactory = engineJobFactory;
if (decodeJobFactory == null) {
decodeJobFactory = new DecodeJobFactory(diskCacheProvider);
}
this.decodeJobFactory = decodeJobFactory;
if (resourceRecycler == null) {
resourceRecycler = new ResourceRecycler();
}
this.resourceRecycler = resourceRecycler;
cache.setResourceRemovedListener(this);
}
構(gòu)造方法中赡艰,初始化了部分成員變量和一些構(gòu)造類的工廠,還包括一些輔助的Resource回收的類ResourceRecycler斤葱,其中比較重要的就是Cache慷垮、KeyFactory、EngineJob和DecodeJob苦掘。下面會(huì)看到在load方法中换帜,如何去使用的。
2.Engine#load
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb) {
Util.assertMainThread();
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
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);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
這段代碼第一是通過(guò)keyFactory構(gòu)建一個(gè)EngineKey鹤啡,這個(gè)key信息包含了以下幾個(gè)部分:
model:此時(shí)是圖片url
signature: 額外的數(shù)據(jù)惯驼,能夠和memory與disk的緩存key進(jìn)行再一次混合,當(dāng)緩存失效時(shí)候递瑰,可以進(jìn)行更多的控制祟牲。一般情況下默認(rèn)實(shí)現(xiàn)是EmptySignature。
width/height:加載的尺寸
transformations: 是一個(gè)Map<Class<?>, Transformation<?>>鍵值對(duì)抖部,Transformation也是可以和memory與disk的緩存key進(jìn)行進(jìn)一步混合说贝,添加額外的信息。在此時(shí)它里面放置了以下信息:
Bitmap.class -> FitCenter
Drawable.class -> DrawableTransformation
BitmapDrawable.class -> DrawableTransformation
GifDrawable.class -> GifDrawableTransformation
resourceClass: 此時(shí)為Object.class. 未知
transcodeClass: 指定了需要返回的Resource類型
options:Options類也是一個(gè)實(shí)現(xiàn)了Key的接口慎颗,與ObjectKey略微不同的是它內(nèi)部的數(shù)據(jù)結(jié)構(gòu)是ArrayMap乡恕。此時(shí)ArrayMap存放的是Option -> com.bumptech.glide.load.resource.bitmap.DownsampleStrategy$FitCenter。Option中的成員變量key為'com.bumptech.glide.load.resource.bitmap.Downsampler.DownsampleStrategy'俯萎。
由上述這些對(duì)象共同構(gòu)造一個(gè)EngineKey的對(duì)象傲宜,它是一個(gè)僅在內(nèi)存中使用的緩存key。實(shí)現(xiàn)了equals與hashCode方法夫啊。實(shí)現(xiàn)如下:
@Override
public boolean equals(Object o) {
if (o instanceof EngineKey) {
EngineKey other = (EngineKey) o;
return model.equals(other.model)
&& signature.equals(other.signature)
&& height == other.height
&& width == other.width
&& transformations.equals(other.transformations)
&& resourceClass.equals(other.resourceClass)
&& transcodeClass.equals(other.transcodeClass)
&& options.equals(other.options);
}
return false;
}
@Override
public int hashCode() {
if (hashCode == 0) {
hashCode = model.hashCode();
hashCode = 31 * hashCode + signature.hashCode();
hashCode = 31 * hashCode + width;
hashCode = 31 * hashCode + height;
hashCode = 31 * hashCode + transformations.hashCode();
hashCode = 31 * hashCode + resourceClass.hashCode();
hashCode = 31 * hashCode + transcodeClass.hashCode();
hashCode = 31 * hashCode + options.hashCode();
}
return hashCode;
}
可以看到函卒,由上面各個(gè)部分,決定了equals和hashCode的結(jié)果撇眯。如果有多次請(qǐng)求报嵌,則可以根據(jù)這些屬性生成的EngineKey緩存key虱咧,若能匹配到,則可以復(fù)用這個(gè)緩存結(jié)果锚国。接下來(lái)就是通過(guò)isMemoryCacheable和key去讀取緩存腕巡。在接下來(lái)loadFromActiveResources中,如果能夠拿到EngineResource跷叉,則整個(gè)過(guò)程結(jié)束逸雹,直接使用內(nèi)存緩存即可,下面我們先分析這個(gè)方法的實(shí)現(xiàn)云挟。
3. Engine#loadFromActiveResources
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
這個(gè)方法首先是判斷isMemoryCacheable是否可用,不可用直接就返回null转质,在Engine#load方法中园欣,如果發(fā)現(xiàn)是null,則會(huì)進(jìn)行其他的策略休蟹,比如讀取disk或請(qǐng)求網(wǎng)絡(luò)等等沸枯。如果isMemoryCacheable可用,則從activeResources中去查找赂弓,我們來(lái)看ActiveResources#get實(shí)現(xiàn)绑榴。
@Nullable
EngineResource<?> get(Key key) {
ResourceWeakReference activeRef = activeEngineResources.get(key);
if (activeRef == null) {
return null;
}
EngineResource<?> active = activeRef.get();
if (active == null) {
cleanupActiveReference(activeRef);
}
return active;
}
activeEngineResources是一個(gè)Map<Key, ResourceWeakReference>對(duì)象,而ResourceWeakReference是WeakReference<EngineResource<?>>的一個(gè)子類盈魁,既然有取值的地方翔怎,那么肯定有對(duì)activeEngineResources put內(nèi)容的時(shí)候,在該類中杨耙,我們發(fā)現(xiàn)其activate方法會(huì)向activeEngineResources中put內(nèi)容赤套。什么時(shí)候回觸發(fā)這個(gè)方法,帶著這個(gè)問(wèn)題我們繼續(xù)分析珊膜,顯然容握,首次加載這里get方法會(huì)返回一個(gè)null,因此loadFromActiveResources的結(jié)果也是一個(gè)null车柠。繼續(xù)回到load方法剔氏,此時(shí)loadFromActiveResources返回為null,則繼續(xù)下一步的策略竹祷,進(jìn)入到loadFromCache方法谈跛。它也是如此,如果能夠取到資源溶褪,則本次load結(jié)束币旧,否則繼續(xù)。
4. Engine#loadFromCache
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
這個(gè)方法中猿妈,依舊是對(duì)isMemoryCacheable的簡(jiǎn)單判斷吹菱,接下來(lái)是調(diào)用getEngineResourceFromCache方法去讀取巍虫,這里發(fā)現(xiàn),如果能夠取到資源鳍刷,則activate方法被觸發(fā)占遥,這里解決了上面什么時(shí)候去往activeEngineResources中put內(nèi)容的疑問(wèn),當(dāng)然并非只有一處输瓜。至于cached.acquire這個(gè)暫不深究瓦胎,用到了計(jì)數(shù)的概念,acquired大于0時(shí)候尤揣,表明有地方正在使用resource資源搔啊,其實(shí)這里可以看到,它這個(gè)是一種優(yōu)化的策略北戏,節(jié)省了內(nèi)存資源负芋。細(xì)節(jié)往往容易影響主線,我們繼續(xù)分析getEngineResourceFromCache這個(gè)的實(shí)現(xiàn)嗜愈,參數(shù)也是一個(gè)緩存key旧蛾。
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);
final EngineResource<?> 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 /*isMemoryCacheable*/, true /*isRecyclable*/);
}
return result;
}
這里的cache實(shí)現(xiàn)了MemoryCache接口,是我們的內(nèi)存緩存對(duì)象蠕嫁,具體的實(shí)現(xiàn)類是LruResourceCache實(shí)現(xiàn)了LruCache<Key, Resource<?>>锨天,而在LruCache中,維護(hù)了一個(gè)LinkedHashMap<Key, Resource<?> cache對(duì)象剃毒,顯然有對(duì)cache增刪查詢的操作病袄。這里我們后續(xù)分析對(duì)cache put的操作。這里迟赃,顯然cache.remove(key)會(huì)返回一個(gè)null對(duì)象陪拘,因此整個(gè)方法返回值也是null,繼續(xù)跟進(jìn)load代碼纤壁。
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
由于loadFromCache返回依然為空左刽,接下來(lái)就是從jobs中去尋找,是否存在了一個(gè)EngineJob酌媒,它是一個(gè)通過(guò)添加和刪除回調(diào)以進(jìn)行加載并在加載完成時(shí)通知回調(diào)來(lái)管理加載的類欠痴。如果存在,則可以復(fù)用此次的EngineJob秒咨,一個(gè)EngineJob和ResourceCallback是一個(gè)一對(duì)多的關(guān)系喇辽,addCallback方法的實(shí)現(xiàn)如下:
void addCallback(ResourceCallback cb) {
Util.assertMainThread();
stateVerifier.throwIfRecycled();
if (hasResource) {
cb.onResourceReady(engineResource, dataSource);
} else if (hasLoadFailed) {
cb.onLoadFailed(exception);
} else {
cbs.add(cb);
}
}
這段代碼很清晰,如果已經(jīng)有資源了雨席,則直接返回菩咨,不需要再去請(qǐng)求,大大得到復(fù)用了。如果失敗抽米,則上報(bào)異常信息特占,否則,則會(huì)添加到cbs變量中云茸,說(shuō)明此時(shí)正在加載是目,會(huì)在之后的加載成功或失敗中,觸發(fā)cbs遍歷去回調(diào)各個(gè)Callback标捺。
首次加載中懊纳,顯然jobs中拿到的EngineJob也是空,因此進(jìn)一步分析load亡容,也就是真正去請(qǐng)求了嗤疯。
5. Enging#load
public <R> LoadStatus load(...{
...
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
真正去請(qǐng)求代碼也很簡(jiǎn)單,主要涉及到EngineJob與DecodeJob萍倡,DecodeJob是一個(gè)負(fù)責(zé)從緩存數(shù)據(jù)或原始資源解碼資源并應(yīng)用轉(zhuǎn)換和轉(zhuǎn)碼的類身弊,它實(shí)現(xiàn)了Runnable接口,是真正加載線程的入口列敲。然后是將engineJob緩存至jobs變量中,最后在EngineJob的start方法中請(qǐng)求線程池去執(zhí)行本次任務(wù)帖汞,至此戴而,加載就已經(jīng)被觸發(fā),后面我們繼續(xù)分析加載的核心邏輯DecodeJob的實(shí)現(xiàn)翩蘸∷猓總的來(lái)說(shuō),加載分為了以下幾個(gè)過(guò)程:
- SingleRequest#onSizeReady方法根據(jù)前面RequestBuilder設(shè)置的參數(shù)催首,請(qǐng)求Engine#load方法
- Engine#load方法中扶踊,根據(jù)相關(guān)參數(shù),組裝成一個(gè)EngineKey郎任,用于標(biāo)識(shí)此次請(qǐng)求的緩存key秧耗,首先以這個(gè)key去從當(dāng)前還處理激活狀態(tài)的Resource資源中去尋找,若查找成功舶治,則返回分井;否則,進(jìn)入下一階段霉猛。
- 若從激活狀態(tài)的Resource資源查找失敗尺锚,則進(jìn)一步去MemoryCache中去查找,若查找成功惜浅,則返回瘫辩;否則,進(jìn)入下一階段。
- 若從MemoryCache中查找失敗伐厌,則再?gòu)膉obs中去看是否存在一個(gè)已經(jīng)加載完成或正在加載的EngineJob承绸。若找到,則將回調(diào)設(shè)置到EngineJob以便接收加載成功或失敗的通知弧械;否則八酒,進(jìn)入下一階段。
- 若沒(méi)有查找到EngineJob刃唐,則創(chuàng)建一個(gè)EngineJob以及DecodeJob羞迷,同時(shí)加入到j(luò)obs緩存之中,并最終調(diào)用EngineJob#start方法画饥,觸發(fā)加載線程執(zhí)行真正的加載衔瓮,從遠(yuǎn)端獲取或者是磁盤獲取等。