在开始以前,咱们先了解Java中的四种引用和ReferenceQueue,为何要了解这些知识呢?你们都知道Glide的缓存使用三级缓存,分别是磁盘缓存和两级内存缓存,而Glide的两级内存缓存就是用WeakReference+ReferenceQueue监控GC回收,这里的回收是指JVM在合适的时间就会回收该对象。java
####Java的四种引用 熟悉Java的同窗都知道Java内存管理分为内存分配和内存回收,都不须要咱们负责,垃圾回收的机制主要是看对象是否有引用指向该对象。java对象的引用包括 强引用、软引用、弱引用和虚引用
。算法
强引用(StrongReference) 强引用是指有引用变量指向时永远不会被垃圾回收,JVM宁愿抛出OOM错误也不会回收这种对象。缓存
软引用(SoftReference) 若是一个对象只具备软引用,则内存空间足够,注意是内存空间足够,垃圾回收器就不会回收它;若是内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就能够被程序使用。软引用可用来实现内存敏感的高速缓存,软引用能够和一个引用队列(ReferenceQueue)联合使用,若是软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中,这也就提供给我监控对象是否被回收。网络
弱引用(WeakReference)
弱引用与软引用的区别在于:只具备弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。不过,因为垃圾回收器是一个优先级很低的线程,所以不必定会很快发现那些只具备弱引用的对象,弱引用能够和一个引用队列(ReferenceQueue)联合使用,若是弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中,这也就提供给我监控对象是否被回收。异步
虚引用 虚引用顾名思义,就是形同虚设,与其余几种引用都不一样,虚引用并不会决定对象的生命周期。若是一个对象仅持有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,若是发现它还有虚引用,就会在回收对象的内存以前,把这个虚引用加入到与之 关联的引用队列中。ide
这里就举个弱引用的栗子,其余就不展开说明了?fetch
//引用队列
private ReferenceQueue queue = new ReferenceQueue<Person>();
/**
* 监控对象被回收,由于若是被回收就会就如与之关联的队列中
*/
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();
复制代码
上面是一个监控对象回收,由于若是被回收就会就如与之关联的队列中,接着开启线程制造触发GC,并开启线程监控对象回收,没什么好说的,下面就开始介绍Glide缓存。ui
####Glide缓存 Glide的缓存分为了内存缓存和磁盘缓存,而内存缓存又分了两个模块,Glide缓存的设计思想很是好,不知道人家是怎么想到。this
内存缓存和磁盘缓存相互结合才构成了Glide极佳的图片缓存效果,那么接下来咱们就分别来分析一下这两种缓存的使用方法以及它们的实现原理,看看我画的时序图,第一次画时序图,欢迎指教。 编码
#####缓存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);
//.....此处省略一万行代码
}
复制代码
实际上直接调用 keyFactory.buildKey方法并把一些资源表示相关的参数传进去构建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 的代码并很少,仅仅是建立EngineKey实例,工厂方法模式,说实在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();
}
}
复制代码
能够看到,其实就是重写equals()和hashCode()方法,即只有传入EngineKey的全部参数都相同的状况下才认为是同一个EngineKey对象或者说是同一种资源。
####内存缓存 对于内存缓存的实现,你们应该最早想到的是LruCache算法(Least Recently Used),也叫近期最少使用算法。它的主要算法原理就是把最近使用的对象用强引用存储在LinkedHashMap中,而且把最近最少使用的对象在缓存值达到预设定值以前从内存中移除,没必要多说Glide也是使用了LruCache,可是Glide还结合WeakReference+ReferenceQueue机制。
对于Glide加载资源,那就从Engine.load()方法开始:
public synchronized <R> LoadStatus load(...............) {
//构建缓存Key
EngineKey key = keyFactory.buildKey();
// 第一级内存缓存,资源是否正在使用,里面使用WeakReference+ReferenceQueue机制
1 EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
return null;
}
// 第二级内存缓存,里面就是LruCache机制
2 EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null;
}
// 不然开始异步加载,磁盘缓存或者请求额昂罗
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);
// 开始执行任务
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
复制代码
能够看到,我标志我1调用了 loadFromActiveResources()方法来获取第一级缓存图片,若是获取到就直接调用cb.onResourceReady()方法进行回调。若是没有获取到,则会在我标志我2调用loadFromCache()方法来获取LruCache缓存图片,获取到的话也直接进行回调。只有在两级缓存都没有获取到的状况下,才会继续向下执行,从而开启线程来加载图片的任务。
也就是说,Glide的图片加载过程当中会调用loadFromActiveResources()方法和loadFromCache()来获取内存缓存。固然这两个方法中一个使用的就是LruCache算法,另外一个使用的就是弱引用机制。咱们来看一下它们的源码:
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
//.......此处省略一万行代码
/**
这个方法检测资源是否正在使用
*/
@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方法中,会将已经存在内存缓存中的资源移除以后会加入activeResources缓存中
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
// 从LruCache中获取并移除资源,可能会返回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;
}
/**
* 这个方法是在当资源从LruCache、disLruCache和网络加载成功,将资源加入正在使用中
*
* @param engineJob
* @param key
* @param resource
*/
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
if (resource != null && resource.isMemoryCacheable()) {
//当资源加载成功,将资源加入正在使用中,如:LruCache、disLruCache和网络
activeResources.activate(key, resource);
}
}
/**
* 这个方法会在ActiveResource类中,正在使用的资源被清理时回调过来。
* 而后从新把资源加入LruCache缓存中,后面会讲ActiveResource的实现
*
* @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方法检测当前Key资源是否正在使用,若是存在直接 cb.onResourceReady()方法资源加载完成,不然调用loadFromCache从LruCache中获取资源, 而后在getEngineResourceFromCache()方法中,会将已经存在内存缓存中的资源remove以后会加入activeResources缓存中.因此资源在内存中的缓存只能存在一份。
当咱们从LruResourceCache中获取到缓存图片以后会将它从缓存中移除,而后将缓存图片存储到activeResources当中。 activeResources就是一个弱引用的HashMap,用来缓存正在使用中的图片,咱们能够看到,loadFromActiveResources()方法就是从activeResources这个HashMap当中取值的。 使用activeResources来缓存正在使用中的图片,能够保护这些图片不会被LruCache算法回收掉。 为何会说能够保护这些图片不会被LruCache算法回收掉呢? 由于在ActiveResources中使用WeakReference+ReferenceQueue机制,监控GC回收,若是GC把资源回收,那么ActiveResources 会被再次加入LruCache内存缓存中,从而起到了保护的做用。
当图片加载完成以后,会在EngineJob当中对调用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);
//......此处省略一万行代码
}
复制代码
接着回调到Engine的onEngineJobComplete()方法当中,以下所示:
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()) {
//当资源加载成功,将资源加入正在使用中,如:LruCache、disLruCache和网络
1 activeResources.activate(key, resource);
}
jobs.removeIfCurrent(key, engineJob);
}
复制代码
能够看到我标志1处,调用activeResources的activate方法把回调过来的EngineResourceactivate和Key做为参数一块儿传入activeResources,就是这里写入缓存,固然在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 上面讲了,当资源加载完成,回调到Engine的onEngineJobComplete(),并调用activeResources的activate方法把回调过来的EngineResourceactivate和Key做为参数一块儿传入activeResources,那接下来分析ActiveResources:
final class ActiveResources {
private final boolean isActiveResourceRetentionAllowed;
// 监控GC回收资源线程池
private final Executor monitorClearedResourcesExecutor;
//使用强引用存储ResourceWeakReference
@VisibleForTesting
final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
//引用队列与ResourceWeakReference配合监听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,
//启动监控线程
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;
}
}
}
/**
* 资源加载完成调用此方法写入缓存
*
* @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强引用
activeEngineResources.remove(ref.key);
if (!ref.isCacheable || ref.resource == null) {
return;
}
//从新构建新的资源
EngineResource<?> newResource = new EngineResource<>(ref.resource,
/*isMemoryCacheable=*/ true,
/*isRecyclable=*/ false,
ref.key,
listener);
// 若是资源被回收,有可能会回调Engine资源会被再次加入LruCache内存缓存中
listener.onResourceReleased(ref.key, newResource);
}
}
}
@SuppressWarnings("WeakerAccess")
@Synthetic
void cleanReferenceQueue() {
while (!isShutdown) {
try {
//remove会阻塞当前线程,知道GC回收,将ResourceWeakReference放入队列
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;
/**
* 调用此方法会将references加入与之关联的ReferencesQueue队列,固然这个方法有可能GC也会调用此方法清除references中的对象置为null,
* 因此这里重写WeakReference,使用一我的域保存资源,若是你经过get方法获取对象一定为null
*
*
*/
clear();
}
}
复制代码
}
能够看出在ActiveResources中使用Map保存ResourceWeakReference,即:使用强引用存储ResourceWeakReference,而ResourceWeakReference继承WeakReference,那么为何要从新拓展WeakReference呢?
你们知道文章开头的时候我讲那些知识并非多余的,在ActiveResources中使用WeakReference+ReferenceQueue机制,监控GC回收,若是GC把资源回收,那么会把WeakReference中的对象置为null,并加入与之关联的ReferenceQueue中,当你经过WeakReference.get()方法返回的是null,因此ResourceWeakReference拓展WeakReference的缘由就很明显了,就是使用类的成员变量保存资源对象,若是你经过get方法获取对象一定为null。
####磁盘缓存
前面讲过若是两级内存缓存都没有读取到数据,那么Glide就会开启线程去加载资源,不限于网络,也包括了磁盘:
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;// 保存数据的 key
this.currentData = data;// 保存数据实体
this.currentFetcher = fetcher; // 保存数据的获取器
this.currentDataSource = dataSource;// 数据来源: url 为 REMOTE 类型的枚举, 表示从远程获取
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
// 调用 decodeFromRetrievedData 解析获取的数据
decodeFromRetrievedData();
}
}
复制代码
咱们知道Glide在两级内存缓存没有获取到,那么会开启线程读取加载图片,当图片加载成功,必然会回调onDataFetcherReady方法,然后调用decodeFromRetrievedData方法解析数据:
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
// 1. 调用了 decodeFromData 获取资源,原始数据
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方法则是从原始数据中检测是不是原始数据,若是不是则直接返回已经转换事后的资源数据,若是当前data是原始数据,那么Glide就会执行解码以后再返回数据,可是若是是已经作过变换的数据,那么直接调用notifyEncodeAndRelease方法通知上层资源获取成功,咱们来看看notifyEncodeAndRelease方法:
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
Resource<R> result = resource;
// 1. 回调上层资源准备好了,能够展现
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
// 2. 将数据缓存到磁盘
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
onEncodeComplete();
}
复制代码
能够看到在notifyEncodeAndRelease方法中,首先就是直接调用 notifyComplete(result, dataSource)方法通知上层资源已经准备好了,接着调用onEngineJobComplete方法写入内存缓存,而后 调用DeferredEncodeManager的encode方法进行编码,将数据写入磁盘,因此这里应该就是磁盘缓存的地方:
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()写入磁盘。