在開始之前,我們先了解Java中的四種引用和ReferenceQueue,為什么要了解這些知識(shí)呢?大家都知道Glide的緩存使用三級(jí)緩存,分別是磁盤緩存和兩級(jí)內(nèi)存緩存,而Glide的兩級(jí)內(nèi)存緩存就是用WeakReference+ReferenceQueue監(jiān)控GC回收,這里的回收是指JVM在合適的時(shí)間就會(huì)回收該對(duì)象。
Java的四種引用
熟悉Java的同學(xué)都知道Java內(nèi)存管理分為內(nèi)存分配和內(nèi)存回收,都不需要我們負(fù)責(zé),垃圾回收的機(jī)制主要是看對(duì)象是否有引用指向該對(duì)象。java對(duì)象的引用包括 強(qiáng)引用、軟引用、弱引用和虛引用。
強(qiáng)引用(StrongReference)
強(qiáng)引用是指有引用變量指向時(shí)永遠(yuǎn)不會(huì)被垃圾回收,JVM寧愿拋出OOM錯(cuò)誤也不會(huì)回收這種對(duì)象。軟引用(SoftReference)
如果一個(gè)對(duì)象只具有軟引用,則內(nèi)存空間足夠,注意是內(nèi)存空間足夠,垃圾回收器就不會(huì)回收它;如果內(nèi)存空間不足了,就會(huì)回收這些對(duì)象的內(nèi)存。只要垃圾回收器沒有回收它,該對(duì)象就可以被程序使用。軟引用可用來實(shí)現(xiàn)內(nèi)存敏感的高速緩存,軟引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用,如果軟引用所引用的對(duì)象被垃圾回收器回收,Java虛擬機(jī)就會(huì)把這個(gè)軟引用加入到與之關(guān)聯(lián)的引用隊(duì)列中,這也就提供給我監(jiān)控對(duì)象是否被回收。弱引用(WeakReference)
弱引用與軟引用的區(qū)別在于:只具有弱引用的對(duì)象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它的內(nèi)存。不過,由于垃圾回收器是一個(gè)優(yōu)先級(jí)很低的線程,因此不一定會(huì)很快發(fā)現(xiàn)那些只具有弱引用的對(duì)象,弱引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用,如果弱引用所引用的對(duì)象被垃圾回收,Java虛擬機(jī)就會(huì)把這個(gè)弱引用加入到與之關(guān)聯(lián)的引用隊(duì)列中,這也就提供給我監(jiān)控對(duì)象是否被回收。虛引用
虛引用顧名思義,就是形同虛設(shè),與其他幾種引用都不同,虛引用并不會(huì)決定對(duì)象的生命周期。如果一個(gè)對(duì)象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時(shí)候都可能被垃圾回收器回收。虛引用主要用來跟蹤對(duì)象被垃圾回收器回收的活動(dòng)。虛引用與軟引用和弱引用的一個(gè)區(qū)別在于:虛引用必須和引用隊(duì)列 (ReferenceQueue)聯(lián)合使用。當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還有虛引用,就會(huì)在回收對(duì)象的內(nèi)存之前,把這個(gè)虛引用加入到與之 關(guān)聯(lián)的引用隊(duì)列中。
這里就舉個(gè)弱引用的栗子,其他就不展開說明了?
//引用隊(duì)列
private ReferenceQueue queue = new ReferenceQueue<Person>();
/**
* 監(jiān)控對(duì)象被回收,因?yàn)槿绻换厥站蜁?huì)就如與之關(guān)聯(lián)的隊(duì)列中
*/
private void monitorClearedResources() {
Log.e("tag", "start monitor");
try {
int n = 0;
WeakReference k;
while ((k = (WeakReference) queue.remove()) != null) {
Log.e("tag", (++n) + "回收了:" + k + " object: " + k.get());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
new Thread() {
@Override
public void run() {
monitorClearedResources();
}
}.start();
new Thread() {
@Override
public void run() {
while (true)
new WeakReference<Person>(new Person("hahah"), queue);
}
}.start();
上面是一個(gè)監(jiān)控對(duì)象回收,因?yàn)槿绻换厥站蜁?huì)就如與之關(guān)聯(lián)的隊(duì)列中,接著開啟線程制造觸發(fā)GC,并開啟線程監(jiān)控對(duì)象回收,沒什么好說的,下面就開始介紹Glide緩存。
Glide緩存
Glide的緩存分為了內(nèi)存緩存和磁盤緩存,而內(nèi)存緩存又分了兩個(gè)模塊,Glide緩存的設(shè)計(jì)思想非常好,不知道人家是怎么想到。
- 內(nèi)存緩存和磁盤緩存的作用各不相同
1、內(nèi)存緩存主要是防止重復(fù)將圖片資源讀取到內(nèi)存當(dāng)中。
2、磁盤緩存主要是防止重復(fù)從網(wǎng)絡(luò)讀取數(shù)據(jù)。
內(nèi)存緩存和磁盤緩存相互結(jié)合才構(gòu)成了Glide極佳的圖片緩存效果,那么接下來我們就分別來分析一下這兩種緩存的使用方法以及它們的實(shí)現(xiàn)原理,看看我畫的時(shí)序圖,第一次畫時(shí)序圖,歡迎指教。

緩存Key
既然是緩存功能,那得有緩存的Key。那么Glide的緩存Key是怎么生成的呢?生成緩存Key的代碼在Engine類的load()方法中:
public synchronized <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, Executor callbackExecutor) {
//.....此處省略一萬行代碼
EngineKey key = keyFactory.buildKey(model, signature, width,height,
transformations,resourceClass, transcodeClass, options);
//.....此處省略一萬行代碼
}
實(shí)際上直接調(diào)用 keyFactory.buildKey方法并把一些資源表示相關(guān)的參數(shù)傳進(jìn)去構(gòu)建EngineKey ,那我們來分析keyFactory.buildKey和EngineKey 的源碼:
EngineKeyFactory .buildKey:
class EngineKeyFactory {
EngineKey buildKey(Object model, Key signature, int width, int height,
Map<Class<?>, Transformation<?>> transformations, Class<?> resourceClass,
Class<?> transcodeClass, Options options) {
return new EngineKey(model, signature, width, height, transformations, resourceClass,
transcodeClass, options);
}
}
可以看到EngineKeyFactory 的代碼并不多,僅僅是創(chuàng)建EngineKey實(shí)例,工廠方法模式,說實(shí)在Glid中用了好多工廠方法模式。
EngineKey :
class EngineKey implements Key {
EngineKey( Object model,Key signature, int width, int height,
Map<Class<?>, Transformation<?>> transformations, Class<?> resourceClass,
Class<?> transcodeClass,Options options) {
// 此處省略一萬行代碼
}
@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;
}
@Override
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
throw new UnsupportedOperationException();
}
}
可以看到,其實(shí)就是重寫equals()和hashCode()方法,即只有傳入EngineKey的所有參數(shù)都相同的情況下才認(rèn)為是同一個(gè)EngineKey對(duì)象或者說是同一種資源。
內(nèi)存緩存
對(duì)于內(nèi)存緩存的實(shí)現(xiàn),大家應(yīng)該最先想到的是LruCache算法(Least Recently Used),也叫近期最少使用算法。它的主要算法原理就是把最近使用的對(duì)象用強(qiáng)引用存儲(chǔ)在LinkedHashMap中,并且把最近最少使用的對(duì)象在緩存值達(dá)到預(yù)設(shè)定值之前從內(nèi)存中移除,不必多說Glide也是使用了LruCache,但是Glide還結(jié)合WeakReference+ReferenceQueue機(jī)制。
對(duì)于Glide加載資源,那就從Engine.load()方法開始:
public synchronized <R> LoadStatus load(...............) {
//構(gòu)建緩存Key
EngineKey key = keyFactory.buildKey();
// 第一級(jí)內(nèi)存緩存,資源是否正在使用,里面使用WeakReference+ReferenceQueue機(jī)制
1 EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
return null;
}
// 第二級(jí)內(nèi)存緩存,里面就是LruCache機(jī)制
2 EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null;
}
// 否則開始異步加載,磁盤緩存或者請(qǐng)求額昂羅
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
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, callbackExecutor);
// 開始執(zhí)行任務(wù)
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
可以看到,我標(biāo)志我1調(diào)用了 loadFromActiveResources()方法來獲取第一級(jí)緩存圖片,如果獲取到就直接調(diào)用cb.onResourceReady()方法進(jìn)行回調(diào)。如果沒有獲取到,則會(huì)在我標(biāo)志我2調(diào)用loadFromCache()方法來獲取LruCache緩存圖片,獲取到的話也直接進(jìn)行回調(diào)。只有在兩級(jí)緩存都沒有獲取到的情況下,才會(huì)繼續(xù)向下執(zhí)行,從而開啟線程來加載圖片的任務(wù)。
也就是說,Glide的圖片加載過程中會(huì)調(diào)用loadFromActiveResources()方法和loadFromCache()來獲取內(nèi)存緩存。當(dāng)然這兩個(gè)方法中一個(gè)使用的就是LruCache算法,另一個(gè)使用的就是弱引用機(jī)制。我們來看一下它們的源碼:
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
//.......此處省略一萬行代碼
/**
這個(gè)方法檢測(cè)資源是否正在使用
*/
@Nullable
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
// 在getEngineResourceFromCache方法中,會(huì)將已經(jīng)存在內(nèi)存緩存中的資源移除之后會(huì)加入activeResources緩存中
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
// 從LruCache中獲取并移除資源,可能會(huì)返回null
Resource<?> cached = cache.remove(key);
final EngineResource<?> result;
//如果返回沒有資源,直接返回null
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, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
}
return result;
}
/**
* 這個(gè)方法是在當(dāng)資源從LruCache、disLruCache和網(wǎng)絡(luò)加載成功,將資源加入正在使用中
*
* @param engineJob
* @param key
* @param resource
*/
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
if (resource != null && resource.isMemoryCacheable()) {
//當(dāng)資源加載成功,將資源加入正在使用中,如:LruCache、disLruCache和網(wǎng)絡(luò)
activeResources.activate(key, resource);
}
}
/**
* 這個(gè)方法會(huì)在ActiveResource類中,正在使用的資源被清理時(shí)回調(diào)過來。
* 然后重新把資源加入LruCache緩存中,后面會(huì)講ActiveResource的實(shí)現(xiàn)
*
* @param cacheKey
* @param resource
*/
@Override
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isMemoryCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
//.......此處省略一萬行代碼
}
首先loadFromActiveResources方法檢測(cè)當(dāng)前Key資源是否正在使用,如果存在直接 cb.onResourceReady()方法資源加載完成,否則調(diào)用loadFromCache從LruCache中獲取資源, 然后在getEngineResourceFromCache()方法中,會(huì)將已經(jīng)存在內(nèi)存緩存中的資源remove之后會(huì)加入activeResources緩存中.所以資源在內(nèi)存中的緩存只能存在一份。
當(dāng)我們從LruResourceCache中獲取到緩存圖片之后會(huì)將它從緩存中移除,然后將緩存圖片存儲(chǔ)到activeResources當(dāng)中。
activeResources就是一個(gè)弱引用的HashMap,用來緩存正在使用中的圖片,我們可以看到,loadFromActiveResources()方法就是從activeResources這個(gè)HashMap當(dāng)中取值的。 使用activeResources來緩存正在使用中的圖片,可以保護(hù)這些圖片不會(huì)被LruCache算法回收掉。 為什么會(huì)說可以保護(hù)這些圖片不會(huì)被LruCache算法回收掉呢? 因?yàn)樵贏ctiveResources中使用WeakReference+ReferenceQueue機(jī)制,監(jiān)控GC回收,如果GC把資源回收,那么ActiveResources 會(huì)被再次加入LruCache內(nèi)存緩存中,從而起到了保護(hù)的作用。
當(dāng)圖片加載完成之后,會(huì)在EngineJob當(dāng)中對(duì)調(diào)用onResourceReady()方法:
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
notifyCallbacksOfResult();
}
接下來就是notifyCallbacksOfResult()方法
void notifyCallbacksOfResult() {
//......此處省略一萬行代碼
engineJobListener.onEngineJobComplete(this, localKey, localResource);
//......此處省略一萬行代碼
}
接著回調(diào)到Engine的onEngineJobComplete()方法當(dāng)中,如下所示:
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
// A null resource indicates that the load failed, usually due to an exception.
if (resource != null && resource.isMemoryCacheable()) {
//當(dāng)資源加載成功,將資源加入正在使用中,如:LruCache、disLruCache和網(wǎng)絡(luò)
1 activeResources.activate(key, resource);
}
jobs.removeIfCurrent(key, engineJob);
}
可以看到我標(biāo)志1處,調(diào)用activeResources的activate方法把回調(diào)過來的EngineResourceactivate和Key作為參數(shù)一起傳入activeResources,就是這里寫入緩存,當(dāng)然在loadFromCache方法中也有寫入緩存:
ActiveResources:
synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut = new ResourceWeakReference(key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
ActiveResources
上面講了,當(dāng)資源加載完成,回調(diào)到Engine的onEngineJobComplete(),并調(diào)用activeResources的activate方法把回調(diào)過來的EngineResourceactivate和Key作為參數(shù)一起傳入activeResources,那接下來分析ActiveResources:
final class ActiveResources {
private final boolean isActiveResourceRetentionAllowed;
// 監(jiān)控GC回收資源線程池
private final Executor monitorClearedResourcesExecutor;
//使用強(qiáng)引用存儲(chǔ)ResourceWeakReference
@VisibleForTesting
final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
//引用隊(duì)列與ResourceWeakReference配合監(jiān)聽GC回收
private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();
private ResourceListener listener;
private volatile boolean isShutdown;
@Nullable
private volatile DequeuedResourceCallback cb;
ActiveResources(boolean isActiveResourceRetentionAllowed) {
this(
isActiveResourceRetentionAllowed,
//啟動(dòng)監(jiān)控線程
java.util.concurrent.Executors.newSingleThreadExecutor(
new ThreadFactory() {
@Override
public Thread newThread(@NonNull final Runnable r) {
return new Thread(
new Runnable() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
r.run();
}
},
"glide-active-resources");
}
}));
}
@VisibleForTesting
ActiveResources(boolean isActiveResourceRetentionAllowed, Executor monitorClearedResourcesExecutor) {
this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
this.monitorClearedResourcesExecutor = monitorClearedResourcesExecutor;
monitorClearedResourcesExecutor.execute(
new Runnable() {
@Override
public void run() {
cleanReferenceQueue();
}
});
}
void setListener(ResourceListener listener) {
synchronized (listener) {
synchronized (this) {
this.listener = listener;
}
}
}
/**
* 資源加載完成調(diào)用此方法寫入緩存
*
* @param key
* @param resource
*/
synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
/**
* 移除指定Key的緩存
*
* @param key
*/
synchronized void deactivate(Key key) {
ResourceWeakReference removed = activeEngineResources.remove(key);
if (removed != null) {
removed.reset();
}
}
/**
* 通過Key獲取緩存
*
* @param key
* @return
*/
@Nullable
synchronized 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;
}
@SuppressWarnings({"WeakerAccess", "SynchronizeOnNonFinalField"})
@Synthetic
void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
// Fixes a deadlock where we normally acquire the Engine lock and then the ActiveResources lock
// but reverse that order in this one particular test. This is definitely a bit of a hack...
synchronized (listener) {
synchronized (this) {
//將Reference移除HashMap強(qiáng)引用
activeEngineResources.remove(ref.key);
if (!ref.isCacheable || ref.resource == null) {
return;
}
//重新構(gòu)建新的資源
EngineResource<?> newResource = new EngineResource<>(ref.resource,
/*isMemoryCacheable=*/ true,
/*isRecyclable=*/ false,
ref.key,
listener);
// 如果資源被回收,有可能會(huì)回調(diào)Engine資源會(huì)被再次加入LruCache內(nèi)存緩存中
listener.onResourceReleased(ref.key, newResource);
}
}
}
@SuppressWarnings("WeakerAccess")
@Synthetic
void cleanReferenceQueue() {
while (!isShutdown) {
try {
//remove會(huì)阻塞當(dāng)前線程,知道GC回收,將ResourceWeakReference放入隊(duì)列
ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
cleanupActiveReference(ref);
// This section for testing only.
DequeuedResourceCallback current = cb;
if (current != null) {
current.onResourceDequeued();
}
// End for testing only.
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
@VisibleForTesting
void setDequeuedResourceCallback(DequeuedResourceCallback cb) {
this.cb = cb;
}
@VisibleForTesting
interface DequeuedResourceCallback {
void onResourceDequeued();
}
@VisibleForTesting
void shutdown() {
isShutdown = true;
if (monitorClearedResourcesExecutor instanceof ExecutorService) {
ExecutorService service = (ExecutorService) monitorClearedResourcesExecutor;
Executors.shutdownAndAwaitTermination(service);
}
}
@VisibleForTesting
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);
this.key = Preconditions.checkNotNull(key);
this.resource = referent.isMemoryCacheable() && isActiveResourceRetentionAllowed ? Preconditions.checkNotNull(referent.getResource()) : null;
isCacheable = referent.isMemoryCacheable();
}
void reset() {
resource = null;
/**
* 調(diào)用此方法會(huì)將references加入與之關(guān)聯(lián)的ReferencesQueue隊(duì)列,當(dāng)然這個(gè)方法有可能GC也會(huì)調(diào)用此方法清除references中的對(duì)象置為null,
* 所以這里重寫WeakReference,使用一個(gè)人域保存資源,如果你通過get方法獲取對(duì)象必定為null
*
*
*/
clear();
}
}
}
可以看出在ActiveResources中使用Map保存ResourceWeakReference,即:使用強(qiáng)引用存儲(chǔ)ResourceWeakReference,而ResourceWeakReference繼承WeakReference,那么為什么要重新拓展WeakReference呢?
大家知道文章開頭的時(shí)候我講那些知識(shí)并不是多余的,在ActiveResources中使用WeakReference+ReferenceQueue機(jī)制,監(jiān)控GC回收,如果GC把資源回收,那么會(huì)把WeakReference中的對(duì)象置為null,并加入與之關(guān)聯(lián)的ReferenceQueue中,當(dāng)你通過WeakReference.get()方法返回的是null,所以ResourceWeakReference拓展WeakReference的原因就很明顯了,就是使用類的成員變量保存資源對(duì)象,如果你通過get方法獲取對(duì)象必定為null。
磁盤緩存
前面講過如果兩級(jí)內(nèi)存緩存都沒有讀取到數(shù)據(jù),那么Glide就會(huì)開啟線程去加載資源,不限于網(wǎng)絡(luò),也包括了磁盤:
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;// 保存數(shù)據(jù)的 key
this.currentData = data;// 保存數(shù)據(jù)實(shí)體
this.currentFetcher = fetcher; // 保存數(shù)據(jù)的獲取器
this.currentDataSource = dataSource;// 數(shù)據(jù)來源: url 為 REMOTE 類型的枚舉, 表示從遠(yuǎn)程獲取
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
// 調(diào)用 decodeFromRetrievedData 解析獲取的數(shù)據(jù)
decodeFromRetrievedData();
}
}
我們知道Glide在兩級(jí)內(nèi)存緩存沒有獲取到,那么會(huì)開啟線程讀取加載圖片,當(dāng)圖片加載成功,必然會(huì)回調(diào)onDataFetcherReady方法,而后調(diào)用decodeFromRetrievedData方法解析數(shù)據(jù):
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
// 1. 調(diào)用了 decodeFromData 獲取資源,原始數(shù)據(jù)
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
// 2. 通知外界資源獲取成功了
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
decodeFromRetrievedData方法則是從原始數(shù)據(jù)中檢測(cè)是否是原始數(shù)據(jù),如果不是則直接返回已經(jīng)轉(zhuǎn)換過后的資源數(shù)據(jù),如果當(dāng)前data是原始數(shù)據(jù),那么Glide就會(huì)執(zhí)行解碼之后再返回?cái)?shù)據(jù),但是如果是已經(jīng)做過變換的數(shù)據(jù),那么直接調(diào)用notifyEncodeAndRelease方法通知上層資源獲取成功,我們來看看notifyEncodeAndRelease方法:
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
Resource<R> result = resource;
// 1. 回調(diào)上層資源準(zhǔn)備好了,可以展示
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
// 2. 將數(shù)據(jù)緩存到磁盤
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
onEncodeComplete();
}
可以看到在notifyEncodeAndRelease方法中,首先就是直接調(diào)用 notifyComplete(result, dataSource)方法通知上層資源已經(jīng)準(zhǔn)備好了,接著調(diào)用onEngineJobComplete方法寫入內(nèi)存緩存,然后 調(diào)用DeferredEncodeManager的encode方法進(jìn)行編碼,將數(shù)據(jù)寫入磁盤,所以這里應(yīng)該就是磁盤緩存的地方:
void encode(DiskCacheProvider diskCacheProvider, Options options) {
GlideTrace.beginSection("DecodeJob.encode");
try {
// 寫入磁盤緩存
diskCacheProvider.getDiskCache().put(key, new DataCacheWriter<>(encoder, toEncode, options));
} finally {
toEncode.unlock();
GlideTrace.endSection();
}
}
在encode方法中 diskCacheProvider.getDiskCache().put()寫入磁盤。