前言
在上一篇文章中介紹了Glide基本的呼叫流程,總結起來就是Engine
是真正載入資源的入口,SingleRequest
起到連線RequestManager
、Target
和Engine
的紐帶關係,本文將承接上文,探討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和容器,諸如engineJobFactory
、decodeJobFactory
和Jobs
、activeResources
等,各種引數先不一一介紹,我們看load()方法,這是整個呼叫的出發點;
我在程式碼中已經註釋的很清晰,下面再梳理一遍流程:
load流程
- 通過keyFactory和請求引數,建立EngineKey物件key;
- 呼叫
loadFromActiveResources()
方法,嘗試從活動的Resources中獲取active; - 如果步驟2命中,直接回撥cb.onResourceReady(),並返回,不命中,執行步驟4;
- 呼叫
loadFromCache()
方法,嘗試從記憶體快取中獲取cached; - 如果步驟4命中,直接回撥cb.onResourceReday(),並返回,不命中,執行步驟6;
- 嘗試從jobs中獲取匹配key的正在執行的EngineJob current;
- 如果步驟6命中,把回撥新增到current並返回,不命中,執行步驟8;
- 通過engineJobFactory建立新的EngineJob物件engineJob;
- 根據decodeJobFactory建立新的DecodeJob物件decodeJob;
- 把engineJob新增進jobs中,講回撥cb設定到engineJob中;
- 執行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分析一定要區分EngineResource
和Resource
,這倆不是一個物件;
牛掰的弱引用複用機制
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)
解除ref
對resource
強引用。
記憶體快取第二階段
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
物件,急於分析流程,我們直接進入DecodeJob
的run()
方法開始:
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
就是來區分這一狀態的:
- INITIALIZE 第一次呼叫run,這次執行的目的是從CACHE中獲取快取
- SWITCH_TO_SOURCE_SERVICE 如果從CACHE中獲取失敗,轉成從資料來源讀取,是第二次呼叫run
- DECODE_DATA 快取獲取成功,對資料進行解析
分階段讀取DiskCache
接下來分析DiskCache,後面出現快取
和Cache
的字眼,如無特殊標識,都是指DiskCache
;
在讀取快取之前,還有兩個概念:stage
和Generator
,在搞清除概念之前,先講一下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
,最終會執行這個Generator
的startNext()
方法,否則,while迴圈獲取loadData
,如果滿足條件,執行loadData.fetcher.loadData(helper.getPriority(), this);
,最後一個引數this
是DataFetcher.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
是誰?cb
是FetcherReadyCallback
型別,這個cb
在SourceGenerator
構造方法中傳入,SourceGenerator
是在DecodeJob
中建立的,真正是cb
是DecodeJob
,所以程式碼定位到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
的主要流程:
- 通過 helper.getLoadData()獲取loadData,呼叫loadData.fetcher.loadData()來執行load;
- DecodeJob接受load回撥,如果需要進行cache,回撥EngineJob執行cache相關的Generator;
- 如果可以解析,呼叫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
物件,decodeHelper
是DecodeHelper
物件,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,完畢。