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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 197,966评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,170评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,909评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,959评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,851评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,583评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,956评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,590评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,878评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,892评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,719评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,501评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,957评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,124评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,440评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,003评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,211评论 2 339

推荐阅读更多精彩内容