Glide4.8原始碼拆解(二)核心載入流程

HitenDev發表於2019-01-03

前言

上一篇文章中介紹了Glide基本的呼叫流程,總結起來就是Engine是真正載入資源的入口,SingleRequest起到連線RequestManagerTargetEngine的紐帶關係,本文將承接上文,探討Glide的載入流程。

本章要討論的內容:

  • Engine的工作流程;
  • 記憶體快取ActiveResource原理剖析;
  • 記憶體快取MemoryCache基本原理;
  • EngineJob和DecodeJob的工作原理;

在討論Engine之前,還是從呼叫它的地方開始SingleReques.onSizeReady

從Engine開始

SingleRequest.java

@Override  public void onSizeReady(int width, int height) { 
stateVerifier.throwIfRecycled();
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));

} if (status != Status.WAITING_FOR_SIZE) {
return;

} status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));

} //建立Engine物件 loadStatus = engine.load( glideContext, model, requestOptions.getSignature(), this.width, this.height, requestOptions.getResourceClass(), transcodeClass, priority, requestOptions.getDiskCacheStrategy(), requestOptions.getTransformations(), requestOptions.isTransformationRequired(), requestOptions.isScaleOnlyOrNoTransform(), requestOptions.getOptions(), requestOptions.isMemoryCacheable(), requestOptions.getUseUnlimitedSourceGeneratorsPool(), requestOptions.getUseAnimationPool(), requestOptions.getOnlyRetrieveFromCache(), this);
//最後一個this是回撥介面 // have completed asynchronously. if (status != Status.RUNNING) {
loadStatus = null;

} if (IS_VERBOSE_LOGGABLE) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));

}
} 複製程式碼

Engine.load()方法有很多引數,其中大部分是從requestOptions中獲得,值得注意的是最後一個引數ResourceCallback,,由於Engine的快取載入邏輯是非同步的,所以SingleRequest得到Engine的結果就全在實現方法onResourceReady()onLoadFailed()裡了;SingleRequest的回撥不再講解,我們要往底層探索,從Engine這個類開始;注意,Engine.load()的呼叫還在主執行緒中;

Engine的初始化和載入流程

Engine.java

public class Engine implements EngineJobListener,    MemoryCache.ResourceRemovedListener,    EngineResource.ResourceListener { 
private final Jobs jobs;
private final MemoryCache cache;
private final EngineJobFactory engineJobFactory;
private final ResourceRecycler resourceRecycler;
private final LazyDiskCacheProvider diskCacheProvider;
private final DecodeJobFactory decodeJobFactory;
private final ActiveResources activeResources;
//構造方法 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;
//建立diskCacheProvider this.diskCacheProvider = new LazyDiskCacheProvider(diskCacheFactory);
//建立activeResources if (activeResources == null) {
activeResources = new ActiveResources(isActiveResourceRetentionAllowed);

} this.activeResources = activeResources;
//監聽 activeResources.setListener(this);
//建立EngineKeyFactory() if (keyFactory == null) {
keyFactory = new EngineKeyFactory();

} this.keyFactory = keyFactory;
//建立Jobs if (jobs == null) {
jobs = new Jobs();

} this.jobs = jobs;
//建立engineJobFactory if (engineJobFactory == null) {
engineJobFactory = new EngineJobFactory( diskCacheExecutor, sourceExecutor, sourceUnlimitedExecutor, animationExecutor, this);

} this.engineJobFactory = engineJobFactory;
//建立decodeJobFactory if (decodeJobFactory == null) {
decodeJobFactory = new DecodeJobFactory(diskCacheProvider);

} this.decodeJobFactory = decodeJobFactory;
//建立resourceRecycler if (resourceRecycler == null) {
resourceRecycler = new ResourceRecycler();

} this.resourceRecycler = resourceRecycler;
//監聽 cache.setResourceRemovedListener(this);

} 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;
//獲得key EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);
//從當前正在使用的Resources裡面去 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);

} //返回null 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);

} //返回null return null;

} //以上都沒有命中,試圖從已存在的任務中對應的EngineJob EngineJob<
?>
current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
//如果去到,把cb往下傳遞 current.addCallback(cb);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);

} //返回結果 return new LoadStatus(cb, current);

} //取不到建立下新的EngineJob EngineJob<
R>
engineJob = engineJobFactory.build( key, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache);
//建立新的DecodeJob DecodeJob<
R>
decodeJob = decodeJobFactory.build( glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, onlyRetrieveFromCache, options, engineJob);
//將當前的engineJob新增到快取中 jobs.put(key, engineJob);
//回撥往下傳遞 engineJob.addCallback(cb);
//engineJob開始執行 engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);

} return new LoadStatus(cb, engineJob);

}
}複製程式碼

在Engine的構造方法中,建立了預設的各種factory和容器,諸如engineJobFactorydecodeJobFactoryJobsactiveResources等,各種引數先不一一介紹,我們看load()方法,這是整個呼叫的出發點;
我在程式碼中已經註釋的很清晰,下面再梳理一遍流程:

load流程

  1. 通過keyFactory和請求引數,建立EngineKey物件key;
  2. 呼叫loadFromActiveResources()方法,嘗試從活動的Resources中獲取active;
  3. 如果步驟2命中,直接回撥cb.onResourceReady(),並返回,不命中,執行步驟4;
  4. 呼叫loadFromCache()方法,嘗試從記憶體快取中獲取cached;
  5. 如果步驟4命中,直接回撥cb.onResourceReday(),並返回,不命中,執行步驟6;
  6. 嘗試從jobs中獲取匹配key的正在執行的EngineJob current;
  7. 如果步驟6命中,把回撥新增到current並返回,不命中,執行步驟8;
  8. 通過engineJobFactory建立新的EngineJob物件engineJob;
  9. 根據decodeJobFactory建立新的DecodeJob物件decodeJob;
  10. 把engineJob新增進jobs中,講回撥cb設定到engineJob中;
  11. 執行engineJob.start(decodeJob);

接下來我們從loadFromActiveResources開始,分析感興趣的方法

記憶體快取第一階段

主要是loadFromActiveResources()流程

Engine.java

private EngineResource<
?>
loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;

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

} return active;

}複製程式碼

ActiveResource的要從activeResources中獲取,activeResources在Engine構造方法中建立,我們分析ActiveResource類的簡單實現;

ActiveResource.java

final class ActiveResources { 
private static final int MSG_CLEAN_REF = 1;
final Map<
Key, ResourceWeakReference>
activeEngineResources = new HashMap<
>
();
private ReferenceQueue<
EngineResource<
?>
>
resourceReferenceQueue;
private Thread cleanReferenceQueueThread;
private ResourceListener listener;
//一般engine監聽次方法 //快取的複用在主執行緒中執行 private final Handler mainHandler = new Handler(Looper.getMainLooper(), new Callback() {
@Override public boolean handleMessage(Message msg) {
if (msg.what == MSG_CLEAN_REF) {
cleanupActiveReference((ResourceWeakReference) msg.obj);
return true;

} return false;

}
});
//設定監聽 void setListener(ResourceListener listener) {
this.listener = listener;

} //get方法 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;

} //activate方法,相當於put void activate(Key key, EngineResource<
?>
resource) {
ResourceWeakReference toPut = new ResourceWeakReference( key, resource, getReferenceQueue(), isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
//移除的弱引用物件需要清除強引用
}
} //清除當前被GC的ref物件 void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
Util.assertMainThread();
activeEngineResources.remove(ref.key);
//從集合中移除掉 if (!ref.isCacheable || ref.resource == null) {
return;

} //建立新的物件EngineResource,複用ref.resource, EngineResource<
?>
newResource = new EngineResource<
>
(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false);
newResource.setResourceListener(ref.key, listener);
listener.onResourceReleased(ref.key, newResource);

} //建立resourceReferenceQueue,用來監聽垃圾回收物件 if (resourceReferenceQueue == null) {
resourceReferenceQueue = new ReferenceQueue<
>
();
//建立執行緒監聽弱引用回收對列 cleanReferenceQueueThread = new Thread(new Runnable() {
@SuppressWarnings("InfiniteLoopStatement") @Override public void run() {
//設定執行緒優先順序有後臺執行緒 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
cleanReferenceQueue();

}
}, "glide-active-resources");
cleanReferenceQueueThread.start();

} return resourceReferenceQueue;

} //shutdown中斷執行緒,清除佇列 void shutdown() {
isShutdown = true;
if (cleanReferenceQueueThread == null) {
return;

} cleanReferenceQueueThread.interrupt();
try {
cleanReferenceQueueThread.join(TimeUnit.SECONDS.toMillis(5));
if (cleanReferenceQueueThread.isAlive()) {
throw new RuntimeException("Failed to join in time");

}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();

}
} //清除回收物件@Synthetic void cleanReferenceQueue() {
while (!isShutdown) {
try {
ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
mainHandler.obtainMessage(MSG_CLEAN_REF, ref).sendToTarget();
// This section for testing only. DequeuedResourceCallback current = cb;
if (current != null) {
current.onResourceDequeued();

} // End for testing only.
} catch (InterruptedException e) {
Thread.currentThread().interrupt();

}
}
} //弱引用監聽物件,強引用儲存真正的資源 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);
//儲存key this.key = Preconditions.checkNotNull(key);
//儲存resource,強引用 this.resource = referent.isCacheable() &
&
isActiveResourceRetentionAllowed ? Preconditions.checkNotNull(referent.getResource()) : null;
isCacheable = referent.isCacheable();

} //清除強引用 void reset() {
resource = null;
clear();

}
}
}複製程式碼

ActiveResource快取原理

ActiveResources採用HashMap+WeakRefence方式儲存EngineResource物件,沒有對集合size做限制,在使用WeakReference的時候,建立了一個ReferenceQueue,來記錄被GC回收的EngineResource物件,而且在建立ReferenceQueue時生成了一個後臺執行緒cleanReferenceQueueThread,不斷地執行cleanReferenceQueue()方法,一旦ReferenceQueue取出不為空,便取出ref物件,執行cleanupActiveReference()方法

有必要看一下EngineResource類結構:

EngineResource.java

class EngineResource<
Z>
implements Resource<
Z>
{
private final boolean isCacheable;
private final boolean isRecyclable;
private ResourceListener listener;
private Key key;
private int acquired;
private boolean isRecycled;
private final Resource<
Z>
resource;
//真正的resource interface ResourceListener {
void onResourceReleased(Key key, EngineResource<
?>
resource);

} EngineResource(Resource<
Z>
toWrap, boolean isCacheable, boolean isRecyclable) {
resource = Preconditions.checkNotNull(toWrap);
this.isCacheable = isCacheable;
this.isRecyclable = isRecyclable;

} void setResourceListener(Key key, ResourceListener listener) {
this.key = key;
this.listener = listener;

} Resource<
Z>
getResource() {
return resource;

}
}複製程式碼

本質上EngineResource是對Resource的包裝類,所以下面的gc分析一定要區分EngineResourceResource,這倆不是一個物件;

牛掰的弱引用複用機制

ResourceWeakReference這個類不簡單,它本意是對EngineResource的弱引用,其實在構造它時候,會把EngineResource.resource和EngineResource.key以強引用的形式儲存,所以垃圾回收的是EngineResource,卻回收不掉EngineResource.resource,因為此時resource會被ResourceWeakReference引用;

cleanupActiveReference()首先取出ref.resource,這個物件是強引用,不會被回收,被回收的是ref包裝的EngineResource;
然後建立新的EngineResource包裝真正的resource,最終呼叫資源回收的監聽listener.onResourceReleased(ref.key, newResource),而setListener()Engine構造方法中呼叫;
看一下Engine.onResourceReleased()方法的實現:

Engine.java

public void onResourceReleased(Key cacheKey, EngineResource<
?>
resource) {
Util.assertMainThread();
//把key對應的ResourceWeakReference從Map中移除 activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
//記憶體快取複用 cache.put(cacheKey, resource);

} else {
resourceRecycler.recycle(resource);

}
}複製程式碼

Engine在onResourceReleased時,重新儲存了EngineResource物件,並且在此之前,還呼叫了activeResources.deactivate(cacheKey);

為什麼要deactivate,下面解釋一下原因:

因為在ActiveResources.cleanupActiveReference()中建立新的EngineResource來包裝被回收的EngineResource下面的resource,但是這個resource還在被ref強引用,所以執行activeResources.deactivate(cacheKey)會清除ref多resource的強引用;

弄明白了這些,ActiveResources原理基本上搞明白了;

小結:

ActiveResources採用弱引用的方式,記錄EngineResource的回收情況,同時採取強引用儲存EngineResource.resource,在ActiveResources中會有個後臺執行緒會執行清理工作,一旦發現某個EngineResource被回收,就會拿出其對應的resource,然後建立一個新的EngineResource包裝這個resource,之後回撥給Engine,讓其做記憶體快取,最後Engine呼叫activeResources.deactivate(cacheKey)解除refresource強引用。

記憶體快取第二階段

loadFromCache()流程分析

Engine.java

private EngineResource<
?>
loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {//跳出 return null;

} EngineResource<
?>
cached = getEngineResourceFromCache(key);
if (cached != null) {//如果命中 cached.acquire();
//+1操作 activeResources.activate(key, cached);
//新增到activeResource
} return cached;

} private EngineResource<
?>
getEngineResourceFromCache(Key key) {
Resource<
?>
cached = cache.remove(key);
//從MemoryCache中取 final EngineResource<
?>
result;
if (cached == null) {
result = null;

} else if (cached instanceof EngineResource) {
result = (EngineResource<
?>
) cached;

} else {
//包裝 result = new EngineResource<
>
(cached, true /*isMemoryCacheable*/, true /*isRecyclable*/);

} return result;

}複製程式碼

loadFromCache()呼叫getEngineResourceFromCache()返回cached,如果命中,cached+1,然後把cached存放到activeResources快取中,getEngineResourceFromCache()主要是呼叫MemoryCache.remove()返回cached,然後在轉成EngineResource返回,主要邏輯還是在MemoryCache中;

LruResourceCache

MemoryCache是一個介面,Engine中使用的cache是在GlideBuilder中建立,預設實現類是LruResourceCache

LruResourceCache.java

public class LruResourceCache extends LruCache<
Key, Resource<
?>
>
implements MemoryCache {
private ResourceRemovedListener listener;
//構造方法傳入最大size public LruResourceCache(long size) {
super(size);

} //設定資源被淘汰的監聽 @Override public void setResourceRemovedListener(@NonNull ResourceRemovedListener listener) {
this.listener = listener;

} //淘汰當前item時呼叫 @Override protected void onItemEvicted(@NonNull Key key, @Nullable Resource<
?>
item) {
if (listener != null &
&
item != null) {
listener.onResourceRemoved(item);

}
} //獲取當前item的size,位元組大小 @Override protected int getSize(@Nullable Resource<
?>
item) {
if (item == null) {
return super.getSize(null);

} else {
return item.getSize();

}
} //記憶體不足時呼叫 @SuppressLint("InlinedApi") @Override public void trimMemory(int level) {
if (level >
= android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
clearMemory();

} else if (level >
= android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN || level == android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
trimToSize(getMaxSize() / 2);

}
}
}複製程式碼

LruResourceCache繼承LruCache並實現MemoryCache介面,快取的真正實現在LruCache中,這個類是Glide自己實現的,同樣基於LikeHashMap,沒什麼特殊點,不過可以看出,item快取大小的計算放在resource自身,除此之外還有一個setResourceRemovedListener()方法,用來監聽資源被淘汰時的回撥;

LruResourceCache允許的最大size在構造方法中傳入,從GlideBuilder中可以看出是MemorySizeCalculator.getMemoryCacheSize()決定,MemorySizeCalculator有預設的快取大小計算規則,篇幅有限暫時不講;

LruResourceCache基本上就是正常的LruCache;儲存的是Resource,並不是Bitmap,到現在我們還沒有看到Bitmap;

記憶體快取小結

上面分析了ActiveResources快取和MemoryCache快取,分別使用弱引用技術和LRU技術,構成兩級記憶體快取,兩者有相似之外卻又截然不同。

  • 相同之處:都是基於記憶體做快取,執行在主執行緒;
  • 不同之處:ActiveResources依賴垃圾回收機制做淘汰運算,MemoryCache是強引用有最大記憶體限制,根據最近最少使用規則來淘汰
  • 優先順序:ActiveResources優先順序高於MemoryCache,ActiveResources當中儲存的是活動物件,在ActiveResources某個item被回收時,如果其對應的真正的Resource沒有被回收,會下放到MemoryCache當中,當然,從MemoryCache中取出的值,會存放在ActiveResources中;

真正的Job

上面一直再說記憶體快取,再回顧一下load流程是:當記憶體中不存在快取時,Engine會嘗試從jobs中取EngineJob快取,jobs內部用HashMap儲存EngineJob物件,當EngineJob載入完成或者取消時,從將當前job從map中移除,這一級算不上Resource的快取,只能算是EngineJob的複用,jobs快取這塊就不細說了,下面主要看EngineJob和DecodeJob;

EngineJob.java

public void start(DecodeJob<
R>
decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);

}複製程式碼

EngineJob呼叫start方法執行decodeJob,其中執行decodeJob是非同步任務必然會用到執行緒池,willDecodeFromCache()方法判斷是否從cache中獲取採取不同的執行緒池方案;從上述程式碼不難看出,DecodeJob肯定是Runnable物件,急於分析流程,我們直接進入DecodeJobrun()方法開始:

DecodeJob.java

public void run() { 
GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
DataFetcher<
?>
localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;

} runWrapped();
//run在這裡
} catch (Throwable t) {
if (stage != Stage.ENCODE) {
throwables.add(t);
notifyFailed();

} if (!isCancelled) {
throw t;

}
} finally {
if (localFetcher != null) {
localFetcher.cleanup();

} GlideTrace.endSection();

}
} //根據stage選擇NextGenerator private void runWrapped() {
switch (runReason) {
case INITIALIZE://初始 stage = getNextStage(Stage.INITIALIZE);
//獲取下一個stage currentGenerator = getNextGenerator();
//獲取下一個Generator runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE://從CACHE跳轉到SOURCE runGenerators();
break;
case DECODE_DATA: decodeFromRetrievedData();
//DECODE_DATA break;
default: throw new IllegalStateException("Unrecognized run reason: " + runReason);

}
} //RunReason控制run方法的執行目標 private enum RunReason {
INITIALIZE,//第一次執行 SWITCH_TO_SOURCE_SERVICE,//從CACHE換到SOURCE DECODE_DATA,//解析
} 複製程式碼

run()方法最終呼叫runWrapped()runWrapped()根據runReason選擇不同的操作,runReason控制run方法執行的目標,上面說過EngineJob程式碼線上程池中執行,不同的任務有不同的執行緒池來執行,而這個runReason就是控制當前執行到哪個任務了;

重要:在一次完整的載入流程中,EngineJob的run方法可以被多次呼叫,每次呼叫的操作不一樣,runReason就是來區分這一狀態的:

  1. INITIALIZE 第一次呼叫run,這次執行的目的是從CACHE中獲取快取
  2. SWITCH_TO_SOURCE_SERVICE 如果從CACHE中獲取失敗,轉成從資料來源讀取,是第二次呼叫run
  3. DECODE_DATA 快取獲取成功,對資料進行解析

分階段讀取DiskCache

接下來分析DiskCache,後面出現快取Cache的字眼,如無特殊標識,都是指DiskCache;

在讀取快取之前,還有兩個概念:stageGenerator,在搞清除概念之前,先講一下Glide的DiskCacheStrategy規則:

  • DiskCacheStrategy.NONE 不進行Disk快取
  • DiskCacheStrategy.DATA 只快取原圖
  • DiskCacheStrategy.RESOURCE 只快取多尺寸的圖片
  • DiskCacheStrategy.ALL 原圖和其他尺寸的圖片都快取
  • DiskCacheStrategy.AUTOMATIC 自動(預設行為)

Glide支援快取多尺寸圖片的,這就加大了讀取快取的複雜程度;

有了這些提升,再來看一下Stage

DecodeJob.java

//Stage表示當前run執行到哪一步驟  private enum Stage { 
INITIALIZE,//初始狀態 RESOURCE_CACHE,//剪裁圖Disk快取 DATA_CACHE,//原圖Disk快取 SOURCE,//遠端圖片 ENCODE,//解析狀態 FINISHED,//完成
} //獲取下一個stage private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE://如果支援多尺寸,下一個state就是RESOURCE_CACHE,否則,從RESOURCE_CACHE開始往下判斷 return diskCacheStrategy.decodeCachedResource() ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE://如果支援原圖尺寸,下一個state就是DATA_CACHE,否則,從DATA_CACHE開始往下判斷 return diskCacheStrategy.decodeCachedData() ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
case DATA_CACHE://如果只讀快取,Stage=FINISHED,否則就是SOURCE return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE: case FINISHED: return Stage.FINISHED;
default: throw new IllegalArgumentException("Unrecognized stage: " + current);

}
} //獲取state對應的Generator private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE://多尺寸對應的 return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE://原圖對應的 return new DataCacheGenerator(decodeHelper, this);
case SOURCE://遠端圖片對應的 return new SourceGenerator(decodeHelper, this);
case FINISHED: return null;
default: throw new IllegalStateException("Unrecognized stage: " + stage);

}
}複製程式碼

stage對應了6種狀態,六種狀態代表當前載入到哪一階段:

stage預設進來是INITIALIZE狀態,通過呼叫getNextStage()不斷往下推進,Stage的推進就表示快取的查詢順序;

Stage 階段解釋 DataFetcherGenerator
INITIALIZE 初始狀態
RESOURCE_CACHE 多尺寸快取 ResourceCacheGenerator
DATA_CACHE 原尺寸快取 DataCacheGenerator
SOURCE 資料來源 SourceGenerator
ENCODE 生成resource過程
FINISHED 結束

在獲取CACHE階段,每個Stage對應了一個DataFetcherGenerator,真正觸發載入快取的邏輯在DataFetcherGenerator中,

再回到runWrapped()方法,假設當前stage為INITIALIZE,且使用者設定了多尺寸快取,這樣呼叫getNextStage(Stage.INITIALIZE)時應該得到下一個stage是Stage.RESOURCE_CACHE,從而得到ResourceCacheGenerator物件,剩下就是執行runGenerators()方法:

//根據stage選擇NextGenerator  private void runWrapped() { 
switch (runReason) {
case INITIALIZE://初始 stage = getNextStage(Stage.INITIALIZE);
//獲取下一個stage currentGenerator = getNextGenerator();
//獲取下一個Generator runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE://從CACHE跳轉到SOURCE runGenerators();
break;
case DECODE_DATA: decodeFromRetrievedData();
//DECODE_DATA break;
default: throw new IllegalStateException("Unrecognized run reason: " + runReason);

}
}//迴圈執行Generatorsprivate void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled &
&
currentGenerator != null &
&
!(isStarted = currentGenerator.startNext())) {//迴圈查詢快取過程 stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;

}
} if ((stage == Stage.FINISHED || isCancelled) &
&
!isStarted) {
notifyFailed();

} // onDataFetcherReady.
}複製程式碼

runGenerators()方法中有一個while迴圈,推進了stage的前進,迴圈的主要的判斷是currentGenerator.startNext(),而這個startNext()方法又是讀取Cache的新開端。

上面說到根據不同的stage建立不同的Generator,我們僅以SourceGenerator來分析;

獲取資料:以SourceGenerator為例

SourceGenerator.java

class SourceGenerator implements DataFetcherGenerator,    DataFetcher.DataCallback<
Object>
, DataFetcherGenerator.FetcherReadyCallback {
@Override public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
//快取的判斷
} if (sourceCacheGenerator != null &
&
sourceCacheGenerator.startNext()) {//有快取從快取中獲取 return true;

} sourceCacheGenerator = null;
loadData = null;
// boolean started = false;
while (!started &
&
hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
//獲取loadData if (loadData != null &
&
(helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource()) || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
//通過loadData.fetcher獲取
}
} return started;

} //判斷是否還有下一個ModelLoader private boolean hasNextModelLoader() {
return loadDataListIndex <
helper.getLoadData().size();

} 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);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished encoding source to cache" + ", key: " + originalKey + ", data: " + dataToCache + ", encoder: " + encoder + ", duration: " + LogTime.getElapsedMillis(startTime));

}
} finally {
loadData.fetcher.cleanup();

} sourceCacheGenerator = new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);

}
}複製程式碼

SourceGenerator實現DataFetcherGenerator介面,主要程式碼在startNext()方法當中,startNext()最前面會判斷資料是否已經在快取中,快取存在直接呼叫cacheData()建立DataCacheGenerator,最終會執行這個GeneratorstartNext()方法,否則,while迴圈獲取loadData,如果滿足條件,執行loadData.fetcher.loadData(helper.getPriority(), this);
,最後一個引數thisDataFetcher.DataCallback回撥介面,獲取成功會回撥onDataReady(Object data)方法;

SourceGenerator.java

@Overridepublic void onDataReady(Object data) { 
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null &
&
diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
cb.reschedule();
//如果可以快取,執行cb.reschedule
} else {
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher, loadData.fetcher.getDataSource(), originalKey);
//直接回撥cb.onDataFetcherReady
}
}複製程式碼

onDataReady對資料是否只從快取做判斷,因為當前是獲取資料來源的資料,如果支援disk快取,回撥cb.reschedule(),否則,回撥cb.onDataFetcherReady,那麼這個cb是誰?cbFetcherReadyCallback型別,這個cbSourceGenerator構造方法中傳入,SourceGenerator是在DecodeJob中建立的,真正是cbDecodeJob,所以程式碼定位到DecodeJob.onDataFetcherReady()reschedule()方法.

DecodeJob.java

@Override  public void reschedule() { 
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
//runReason進行到下一步 callback.reschedule(this);
//callback是EngineJob
} @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;
//runReason進行到下一步 callback.reschedule(this);
//解析資料,
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
//真正的解析資料,上面的if最終也會走到這裡
} finally {
GlideTrace.endSection();

}
}
}複製程式碼

reschedule()方法中,首先將runReason推進到SWITCH_TO_SOURCE_SERVICE,然後呼叫EngineJob.reschedule(this),最終還會再次走到run()方法,不同的是在EngineJob中為decodeJob重新分配了執行緒池,另一個不同是runReason;

onDataFetcherReady()方法中,會判斷當前執行緒池,如果不符合條件,也會將runReason推進到DECODE_DATA,然後執行EngineJob.reschedule(this),流程和reschedule()回撥方法一樣,最終會重新走run()方法,run()方法會根據runReason判斷邏輯,如果在當前執行緒池,直接呼叫decodeFromRetrievedData();

我們上面只分析了onDataFetcherReady()也就是資料成功的情況,失敗的情況暫時不討論了,總結一下SourceGenerator的主要流程:

  1. 通過 helper.getLoadData()獲取loadData,呼叫loadData.fetcher.loadData()來執行load;
  2. DecodeJob接受load回撥,如果需要進行cache,回撥EngineJob執行cache相關的Generator;
  3. 如果可以解析,呼叫decodeFromRetrievedData()做解析工作;

提升:onDataFetcherReady()回撥的Object不是Bitmap或者File,因為還沒到這一步驟,一般是InputStream或者ByteBuffer

解析部分

private void decodeFromRetrievedData() { 
... resource = decodeFromData(currentFetcher, currentData, currentDataSource);
...
} private <
Data>
Resource<
R>
decodeFromData(DataFetcher<
?>
fetcher, Data data, DataSource dataSource) throws GlideException {
... Resource<
R>
result = decodeFromFetcher(data, dataSource);
//呼叫decodeFromFetcher ...
} private <
Data>
Resource<
R>
decodeFromFetcher(Data data, DataSource dataSource) throws GlideException {
LoadPath<
Data, ?, R>
path = decodeHelper.getLoadPath((Class<
Data>
) data.getClass());
//獲取loadPath return runLoadPath(data, dataSource, path);

} private <
Data, ResourceType>
Resource<
R>
runLoadPath(Data data, DataSource dataSource, LoadPath<
Data, ResourceType, R>
path) throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder<
Data>
rewinder = glideContext.getRegistry().getRewinder(data);
try {
// ResourceType in DecodeCallback below is required for compilation to work with gradle. return path.load( rewinder, options, width, height, new DecodeCallback<
ResourceType>
(dataSource));
//呼叫path.load
} finally {
rewinder.cleanup();

}
}複製程式碼

解析方法在DecodeJob中幾斤週轉,最終從decodeHelper.getLoadPath()得到LoadPath物件,然後呼叫path.load()執行真正的載入邏輯;

LoadPath.java

public Resource<
Transcode>
load(DataRewinder<
Data>
rewinder, @NonNull Options options, int width, int height, DecodePath.DecodeCallback<
ResourceType>
decodeCallback) throws GlideException {
List<
Throwable>
throwables = Preconditions.checkNotNull(listPool.acquire());
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);

} finally {
listPool.release(throwables);

}
} private Resource<
Transcode>
loadWithExceptionList(DataRewinder<
Data>
rewinder, @NonNull Options options, int width, int height, DecodePath.DecodeCallback<
ResourceType>
decodeCallback, List<
Throwable>
exceptions) throws GlideException {
Resource<
Transcode>
result = null;
//noinspection ForLoopReplaceableByForEach to improve perf for (int i = 0, size = decodePaths.size();
i <
size;
i++) {
DecodePath<
Data, ResourceType, Transcode>
path = decodePaths.get(i);
try {
result = path.decode(rewinder, width, height, options, decodeCallback);
//呼叫用decodePath.decode
} catch (GlideException e) {
exceptions.add(e);

} if (result != null) {
break;

}
} if (result == null) {
throw new GlideException(failureMessage, new ArrayList<
>
(exceptions));

} return result;

}複製程式碼

LoadPath中,最終呼叫loadWithExceptionList()方法,在該方法中,通過迴圈獲取decodePaths中的DecodePath物件,最終判斷result是否為空來標誌結束,所以程式碼從LoadPath轉到decodePath.decode()方法中;

DecodePath.java

public Resource<
Transcode>
decode(DataRewinder<
DataType>
rewinder, int width, int height, @NonNull Options options, DecodeCallback<
ResourceType>
callback) throws GlideException {
Resource<
ResourceType>
decoded = decodeResource(rewinder, width, height, options);
//decodeResource流程在這裡 Resource<
ResourceType>
transformed = callback.onResourceDecoded(decoded);
//transformed結果 return transcoder.transcode(transformed, options);

} private Resource<
ResourceType>
decodeResource(DataRewinder<
DataType>
rewinder, int width, int height, @NonNull Options options) throws GlideException {
List<
Throwable>
exceptions = Preconditions.checkNotNull(listPool.acquire());
try {
return decodeResourceWithList(rewinder, width, height, options, exceptions);

} finally {
listPool.release(exceptions);

}
} //從list中取出能解析的private Resource<
ResourceType>
decodeResourceWithList(DataRewinder<
DataType>
rewinder, int width, int height, @NonNull Options options, List<
Throwable>
exceptions) throws GlideException {
Resource<
ResourceType>
result = null;
//noinspection ForLoopReplaceableByForEach to improve perf for (int i = 0, size = decoders.size();
i <
size;
i++) {
ResourceDecoder<
DataType, ResourceType>
decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
result = decoder.decode(data, width, height, options);

}
} catch (IOException | RuntimeException | OutOfMemoryError e) {
exceptions.add(e);

} if (result != null) {
break;

}
} if (result == null) {
throw new GlideException(failureMessage, new ArrayList<
>
(exceptions));

} return result;

} 複製程式碼

DecodePath邏輯和LoadPath很相似,最終是從decoders中迴圈取出ResourceDecoder物件,然後呼叫decoder.decode()來做最後的解析;另外,拿到解析結果之後,呼叫callback.onResourceDecoded(decoded);
獲取transform結果,這個我們本章節最後分析;

接下來我們分析ResourceDecoder

ResourceDecoder是一個介面

ResourceDecoder

/** * An interface for decoding resources. * * @param <
T>
The type the resource will be decoded from (File, InputStream etc). * @param <
Z>
The type of the decoded resource (Bitmap, Drawable etc). */public interface ResourceDecoder<
T, Z>
{
//通過引數,判斷是否能夠解析 boolean handles(@NonNull T source, @NonNull Options options) throws IOException;
@Nullable Resource<
Z>
decode(@NonNull T source, int width, int height, @NonNull Options options) throws IOException;

}複製程式碼

繼續往下分析之前,先看一下ResourceDecoder<
T,Z>
泛型代表什麼意思,從類註釋上來看,T程式碼需要被解析的型別(例如:File,InputStream),Z是結果的型別(比如 Bitmap,Drawable),知道了這些,是弄清ResourceDecoder的基本前提;既然如此,我希望找到一個T為InputStream,Z為Bitmap的子類,Glide肯定有提供這個方案StreamBitmapDecoder,我們就以StreamBitmapDecoder為代表,分析ResourceDecoder;

分析解析流程

StreamBitmapDecoder.java

public class StreamBitmapDecoder implements ResourceDecoder<
InputStream, Bitmap>
{
@Override public Resource<
Bitmap>
decode(@NonNull InputStream source, int width, int height, @NonNull Options options) throws IOException {
// Use to fix the mark limit to avoid allocating buffers that fit entire images. final RecyclableBufferedInputStream bufferedStream;
final boolean ownsBufferedStream;
//獲取bufferedStream if (source instanceof RecyclableBufferedInputStream) {
bufferedStream = (RecyclableBufferedInputStream) source;
ownsBufferedStream = false;

} else {
bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
ownsBufferedStream = true;

} ExceptionCatchingInputStream exceptionStream = ExceptionCatchingInputStream.obtain(bufferedStream);
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
//callbacks try {
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
//最後的解析
} finally {
exceptionStream.release();
if (ownsBufferedStream) {
bufferedStream.release();

}
}
}
}複製程式碼

decode方法經過一系列的流程,最終呼叫downsampler.decode(),最後的解析流程指向了Downsampler,簡單來看一下這個類的介紹;

Downsampler.java

/** * Downsamples, decodes, and rotates images according to their exif orientation. */public final class Downsampler {
}複製程式碼

這個類主要負責對圖片進行:下采樣,解析和根據exif方向旋轉圖片;關於Downsampler的細節分析不在這裡進行,真個解析的流程分析到這裡;

Transform呼叫

剛才在上文的DecodePath中分析呼叫callBack.onResourceDecoded()進行transform處理,我們簡單分析一下transform的簡單流程:

首先callBack是DecodeJob物件,最終程式碼到DecodeJob.onResourceDecoded()

DecodeJob.java

  <
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;
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();

} ...省略 return result;

}複製程式碼

通過decodeHelper.getTransformation()獲取目標的Transformation物件,decodeHelperDecodeHelper物件,getTransformation()流程分析:

DecodeHelper.java

 <
Z>
Transformation<
Z>
getTransformation(Class<
Z>
resourceClass) {
Transformation<
Z>
result = (Transformation<
Z>
) transformations.get(resourceClass);
//transformations是一個集合,在DecodeJob中初始化 if (result == null) {
for (Entry<
Class<
?>
, Transformation<
?>
>
entry : transformations.entrySet()) {
if (entry.getKey().isAssignableFrom(resourceClass)) {//根據key和resourceClass匹配 result = (Transformation<
Z>
) entry.getValue();
break;

}
}
} if (result == null) {
if (transformations.isEmpty() &
&
isTransformationRequired) {
throw new IllegalArgumentException( "Missing transformation for " + resourceClass + ". If you wish to" + " ignore unknown resource types, use the optional transformation methods.");

} else {
return UnitTransformation.get();

}
} return result;

}複製程式碼

DecodeHelper內部有一個Map集合transformations,這個集合在init()方法中初始化,init()DecodeJob中呼叫,getTransformation()根據Resource型別來遍歷transformations獲取目標Transformation並返回;至於DecodeHelper集合transformations最初的呼叫,是從SingleRequest呼叫Engine.load()傳遞,最本質的來源還是RequestOptions.getTransformations(),所以transformations這個集合賦值流程是:SingleRequest->
Engine->
->
DecodeJob->
DecodeHelper-transformations,完畢。

來源:https://juejin.im/post/5c2dffa8f265da611d66c8b6

相關文章