Glide4.8原始碼拆解(二)核心載入流程
前言
在上一篇文章中介紹了Glide基本的呼叫流程,總結起來就是Engine
是真正載入資源的入口,SingleRequest
起到連線RequestManager
、Target
和Engine
的紐帶關係,本文將承接上文,探討Glide的載入流程。
本章要討論的內容:
- Engine的工作流程;
- 記憶體快取ActiveResource原理剖析;
- 記憶體快取MemoryCache原理剖析;
- EngineJob快取分析;
- 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()流程分析
未完待續睡覺...