glide缓存之ActiveResources

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中。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容