原文可以看我的博客
基于v4最新版本的Glide解析, 從最開始的簡單加載開始看源碼, 僅作個人記錄.
一個Glide加載圖片的核心用法如下:
GlideApp.with(this)
.load(uri)
.into(imageViewLookup);
我們通過一步步鏈式調(diào)用進去查看
Glide.with : 同步生命周期
private RequestManager supportFragmentGet(@NonNull Context context, @NonNull FragmentManager fm,
@Nullable Fragment parentHint) {
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
通過getSupportRequestManagerFragment(final FragmentManager fm, Fragment parentHint)
方法調(diào)用, 在Glide.with(context)
中傳入的組件中,
新增一個子Fragment, 這個Fragment類根據(jù)傳入的是support.fragment
或者是fragment
來決定是RequestManagerFragment
還是SupportRequestManagerFragment
,然后通過current.SupportRequestManagerFragment()
將Glide的生命周期與這個子fragment的聲明周期綁定, 實現(xiàn)了組件與Glide加載同步的功能
圖片的加載
我們通過暴露的into的API跳進去, 最終到了RequestBuilder.into(@NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, @NonNull RequestOptions options)
, 詳細代碼如下:
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
@NonNull RequestOptions options) {
// 判斷是否在主線程
Util.assertMainThread();
// target是否為空判斷
Preconditions.checkNotNull(target);
// load()方法是否已經(jīng)被調(diào)用, 如果沒被調(diào)用, 則將拋出異常
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
options = options.autoClone();
// 創(chuàng)建請求
Request request = buildRequest(target, targetListener, options);
// 獲取target當前的請求
Request previous = target.getRequest();
// 如果請求相同, 而且當前請求設(shè)置可以使用內(nèi)存緩存
// 則請求回收
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
request.recycle();
// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
// 如果當前請求不在執(zhí)行, 則會重新開始請求
if (!Preconditions.checkNotNull(previous).isRunning()) {
// Use the previous request rather than the new one to allow for optimizations like skipping
// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
// that are done in the individual Request.
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
// 請求追蹤
requestManager.track(target, request);
return target;
}
然后通過requestManager.track()
發(fā)起Request執(zhí)行, 如果當前狀態(tài)(status
)既不是RUNNING
也不是COMPLETE
, 則會執(zhí)行onSizeReady
, 到這里直到Engine.load()
才開始資源的加載, 相關(guān)的代碼及注釋如下:
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 = LogTime.getLogTime();
// 創(chuàng)建緩存key
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
// 從存活資源內(nèi)讀取數(shù)據(jù), 內(nèi)部緩存由value為弱引用對象的map做管理, 做手動的計數(shù)管理
// 當資源計數(shù)為0時, 則回收
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
// 如果命中, 則回調(diào)加載
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
// 獲取內(nèi)存緩存數(shù)據(jù)
// 當內(nèi)存緩存中有命中, 則刪除Cache, 并將目標資源加到activeResources中
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
// 如果命中, 則回調(diào)加載
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
// EngineJob : 調(diào)度DecodeJob,添加,移除資源回調(diào),并notify回調(diào)
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
// 當前存活的資源和內(nèi)存緩存都沒有的情況下
// 1. 先判斷是否有資源(resouce什么時候回調(diào)true 不明), 如果有, 則回調(diào)加載
// 2. 如果加載失敗, 則加載拋出異常
// 3. 否則, 在資源回調(diào)中添加
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
// 返回當前的LoadStatus
return new LoadStatus(cb, current);
}
// 當資源回調(diào)中都沒有的情況
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
// 實現(xiàn)了Runnable接口胚泌,調(diào)度任務(wù)的核心類立帖,整個請求的繁重工作都在這里完成:處理來自緩存或者原始的資源绣夺,應(yīng)用轉(zhuǎn)換動畫以及transcode榨馁。
// 負責(zé)根據(jù)緩存類型獲取不同的Generator加載數(shù)據(jù),數(shù)據(jù)加載成功后回調(diào)DecodeJob的onDataFetcherReady方法對資源進行處理
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 (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
這里的流程圖可以看下圖:
資源圖片的緩存
當無法再當前存活的資源以及緩存內(nèi)找到對應(yīng)key的資源時, 會通過engineJob
開始執(zhí)行decodeJob
, 所以我們可以直接看decodeJob
的run()
.
/**
* 根據(jù)不同的runReason執(zhí)行不同任務(wù)
*/
private void runWrapped() {
switch (runReason) {
// 首次請求時
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
// load數(shù)據(jù)
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
// load數(shù)據(jù)
runGenerators();
break;
case DECODE_DATA:
// 數(shù)據(jù)處理
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
核心的執(zhí)行流程如下代碼:
/**
* 執(zhí)行Generators
*/
private void runGenerators() {
// 獲取當前線程
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
// currentGenerator.startNext() : 從當前策略對應(yīng)的Generator獲取數(shù)據(jù)输钩,數(shù)據(jù)獲取成功則回調(diào)DecodeJob的onDataFetcherReady對資源進行處理。否則嘗試從下一個策略的Generator獲取數(shù)據(jù)
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
// 根據(jù)Stage獲取到相應(yīng)的Generator后會執(zhí)行currentGenerator.startNext()仲智,如果中途startNext返回true买乃,則直接回調(diào),否則最終會得到SOURCE的stage坎藐,重新調(diào)度任務(wù)
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
// 重新調(diào)度當前任務(wù)
reschedule();
return;
}
}
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
// Otherwise a generator started a new load and we expect to be called back in
// onDataFetcherReady.
}
我們看下DecodeJob的執(zhí)行流程
總結(jié)
到這里, 整體的流程大致是搞清楚了, 至于說是緩存的原理機制, 在之前Engine.load()
的方法內(nèi), 刪除緩存的方法進去可以看到一個LruCache
的類文件, 從名字可以推斷是Glide自己實現(xiàn)的Lru算法
作為緩存的處理, 關(guān)于Lru的算法原理, 在本篇內(nèi)就不再做贅述了, 而ActiveCache
用到了引用計數(shù)
算法.
Glide用到了大量的抽象工廠類, 另外方法內(nèi)經(jīng)常是包括了十來個參數(shù), 在閱讀的經(jīng)過上還是有點困難(對我而言).
相應(yīng)的代碼注釋可看Github上我補充的注釋