glide 缓存分为内存缓存和硬盘缓存,内存缓存是用Lru算法缓存和弱引用缓存(ActiveResources),在介绍弱引用缓存前,首先看看什么是弱引用
WeakReference
所谓弱引用就是在一次gc过程中,如果遍历到此引用为弱引用,就会将其回收,什么意思呢,具体用代码说明:
public class WeakReferenceTest {
private static HashMap<String, MyWeakReference> weakReferenceHashMap = new HashMap<>();
private static ReferenceQueue<MyObject> referenceQueue = new ReferenceQueue<>();
private static class MyObject {
private byte data[] = new byte[1024];
}
private static class MyWeakReference extends WeakReference<MyObject> {
public MyWeakReference(MyObject referent){
super(referent);
}
}
public static void main(String[] args) {
String key = "1";
weakReferenceHashMap.put(key, new MyWeakReference(new MyObject()));
System.out.println("first MyObject===" + weakReferenceHashMap.get(key).get());
System.out.println("first WeakReference===" + weakReferenceHashMap.get(key));
System.gc();
System.out.println("second MyObject=== " + weakReferenceHashMap.get(key).get());
System.out.println("second WeakReference===" + weakReferenceHashMap.get(key));
}
}
打印的结果如下:
first MyObject===com.example.demo.WeakReferenceTest$MyObject@279f2327
first WeakReference===com.example.demo.WeakReferenceTest$MyWeakReference@2ff4acd0
second MyObject=== null
second WeakReference===com.example.demo.WeakReferenceTest$MyWeakReference@2ff4acd0
我们发现经过gc之后,MyObject对象被回收掉了,所以也就不难理解 WeakReference 的作用了,有什么用处呢,这就引出本文的重点,通过弱引用作为内存缓存的一种方式。
需要注意的是:被弱引用对象关联的对象会自动被垃圾回收器回收,但是弱引用对象本身也是一个对象,这些创建的弱引用并不会自动被垃圾回收器回收掉。
通过上面的执行结果来说明,可以看出 hashMap中所保存的弱引用本身并没有被回收掉(second WeakReference 不为null),所以我们需要使用另外一个构造方法
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
ReferenceQueue,是在对象被回收后,会把弱引用对象,也就是WeakReference对象或者其子类的对象,放入队列ReferenceQueue中,所以改一下上面的代码如下:
public class WeakReferenceTest {
private static HashMap<String, MyWeakReference> weakReferenceHashMap = new HashMap<>();
private static ReferenceQueue<MyObject> referenceQueue = new ReferenceQueue<>();
private static class MyObject {
private byte data[] = new byte[1024];
}
private static class MyWeakReference extends WeakReference<MyObject> {
final String key;
public MyWeakReference(String key, MyObject referent, ReferenceQueue<? super MyObject> q) {
super(referent, q);
this.key = key;
}
}
public static void main(String[] args) {
String key = "1";
weakReferenceHashMap.put(key, new MyWeakReference(key, new MyObject(), referenceQueue));
System.out.println("first MyObject===" + weakReferenceHashMap.get(key).get());
System.out.println("first WeakReference===" + weakReferenceHashMap.get(key));
System.gc();
System.out.println("second MyObject=== " + weakReferenceHashMap.get(key).get());
cleanWeakReference();
System.out.println("second WeakReference===" + weakReferenceHashMap.get(key));
}
private static void cleanWeakReference() {
try {
Thread.sleep(1000);//确保referenceQueue中有值
} catch (InterruptedException e) {
e.printStackTrace();
}
Reference<? extends MyObject> reference = referenceQueue.poll();
if (reference != null) {
weakReferenceHashMap.remove(((MyWeakReference) reference).key);
}
}
}
结果如下:
first MyObject===com.example.demo.WeakReferenceTest$MyObject@279f2327
first WeakReference===com.example.demo.WeakReferenceTest$MyWeakReference@2ff4acd0
second MyObject=== null
second WeakReference===null
发现hashMap中的引用对象被移除掉。
ActiveResources
经过上面对弱引用的用法简单的介绍后,引出 glide 弱引用的缓存如何做的,
关于glide 内存缓存的使用主要在 Engine这个类中:
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);
}
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);
}
return null;
}
首先就是从ActiveResources中获取缓存资源,具体获取方式:
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
//是否开启内存缓存
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
activeResources.get(key) 就是从弱应用缓存中获取正在使用的资源,然后调用acquire(int) ,引用加1,acquire变量大于0,说明图片正在使用,等于0,图片不再使用,后续就会释放缓存的资源,放入Lru中。具体看看activeResources.get(key) 如何做的:
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;
}
就是在activeEngineResources中拿到缓存的EngineResource对象返回,activeEngineResources又是个什么结构:
final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();
这就是开篇对弱引用的介绍,写法和上面的测试代码一致,这里不去过多说明了,然后就是怎么去移除缓存的,或者在gc之后怎么处理activeEngineResources中的资源的,ActiveResources的做法是在构造方法中开启一个线程检测resourceReferenceQueue中是否有资源,有就是代表gc回收的,然后就遍历resourceReferenceQueue,删除掉activeEngineResources的ResourceWeakReference本身。先看ActiveResources构造方法怎么做的:
ActiveResources(
boolean isActiveResourceRetentionAllowed, Executor monitorClearedResourcesExecutor) {
this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
this.monitorClearedResourcesExecutor = monitorClearedResourcesExecutor;
monitorClearedResourcesExecutor.execute(
new Runnable() {
@Override
public void run() {
cleanReferenceQueue();
}
});
}
发现调用了cleanReferenceQueue这个方法,点进去如下:
void cleanReferenceQueue() {
while (!isShutdown) {
try {
ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
cleanupActiveReference(ref);
...删除无关代码
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
resourceReferenceQueue.remove()在队列中没有元素时会阻塞,然后就是cleanupActiveReference(ref);
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) {
activeEngineResources.remove(ref.key);
if (!ref.isCacheable || ref.resource == null) {
return;
}
EngineResource<?> newResource =
new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false);
newResource.setResourceListener(ref.key, listener);
listener.onResourceReleased(ref.key, newResource);
}
}
}
activeEngineResources.remove(ref.key);到此activeEngineResources就删掉了保存的WeakReference。
glide又是什么时候使用弱引用缓存的?
在Engine中看到loadFromCache方法,从Lru中获取资源,具体做了什么呢:
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
我们看到在从Lru中获取之后会执行activeResources.activate(key, cached),保存正在使用的资源到activeEngineResources中。