【笔记整理】Glide 4.9.0 的缓存机制

Glide 类中与内存缓存有关的变量Glide#memoryCache,如果用户没有通过 GlideBuilder#setMemoryCache(MemoryCache) 设置,则默认为 LruResourceCache(继承自 LruCache)。


1. 从内存缓存读取图片资源

Engine#load() 方法中,就先根据需要加载的资源的相关参数,来生成唯一的 key 值,而该值与从缓存机制有关。

// Engine.java
public synchronized <R> LoadStatus load(...) {

  long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
  
  // 首先根据目标资源的相关参数得到对应的 key 值
  EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
      resourceClass, transcodeClass, options);
      
  //(1)先从 ActiveResources 里面去获取
  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;
  }
  
  // (2)如果没有,则从内存缓存中去获取
  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;
  }
  
  // 在两个方法都没有获取到缓存的情况下,才会继续向下执行,从而开启线程来加载图片
  ...
}

(1) loadFromActiveResources()

private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
  // 先判断是否禁用内存缓存
  if (!isMemoryCacheable) {
    return null;
  }
  // 从 activeResources 中去获取资源
  EngineResource<?> active = activeResources.get(key);
  if (active != null) {
    // 标记资源被引用数量 +1
    active.acquire();
  }
  return active;
}

activeResourcesActiveResources 对象,其内部有一个 HashMap<Key, ResourceWeakReference>,即持有着 key 值对应的资源对象的弱引用。

activeResources 的目的是为了 缓存正在使用中的图片,从而可以保护这些图片不会被 LruCache 算法回收掉。

(2) loadFromCache()

private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
  if (!isMemoryCacheable) {
    return null;
  }
  // 从 LruCache 对应的内存缓存中去获取
  EngineResource<?> cached = getEngineResourceFromCache(key);
  if (cached != null) {
    cached.acquire();
    // 将取出的资源添加到 activeResources 中
    activeResources.activate(key, cached);
  }
  return cached;
}

private EngineResource<?> getEngineResourceFromCache(Key key) {
  Resource<?> cached = cache.remove(key);
  final EngineResource<?> result;
  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, true /*isMemoryCacheable*/, true /*isRecyclable*/);
  }
  return result;
}

loadFromCache() 则是从前面说的默认为 LruResourceCache 中去获取目标资源。如果目标资源存在,则如 LruResourceCache 中取出,并加入到前面说的 activeResources 中。

从内存缓存中读取资源的逻辑大概就是这些。概括一下来说,就是如果能从内存缓存当中读取到要加载的图片,那么就直接进行回调,如果读取不到的话,才会开启线程执行后面的图片加载逻辑。

补充,根据郭神的说的,对于内存缓存,使用到 ActiveResourcesLruResourceCache,目的是为了保护正在被使用的图片不会被 LruCache 算法回收掉。


2. 将图片资源缓存到内存

Engine 类实现了 EngineJobListener 接口,当资源加载完成后,就回调 Engine#onEngineJobComplete() 方法。

// Engine.java
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) {
    // 为 EngineResource 资源对象设置 ResourceListener 监听,即 Engine 对象自身,
    // 因为Engine 实现了 ResourceListener 接口。
    resource.setResourceListener(key, this);
    // 如果资源允许被缓存,则将其添加到 activeResources 中
    if (resource.isCacheable()) {
      activeResources.activate(key, resource);
    }
  }
  jobs.removeIfCurrent(key, engineJob);
}

这里注意的是,加载好的资源并不是直接被缓存到 LruResourceCache 中的,而是先被缓存到 activeResources 中。

而缓存到 LruResourceCache 中,则与第 7 行设置 ResourceListener 有关。之前有说过 EngineResource#acquire() 方法,该方法中就使用 EngineResource 对象的成员变量 acquired 来记录图片资源被引用的次数,调用 acquire() 方法会让变量加 1,而调用 release() 方法则会使变量减 1。

也就是说,当 acquired 变量大于 0 的时候,说明图片正在使用中,也就应该放到 activeResources 弱引用缓存当中。而经过 release() 之后,如果 acquired 变量等于 0 了,说明图片已经 不是正在被使用状态 了。

void release() {
  synchronized (listener) {
    synchronized (this) {
      if (acquired <= 0) {
        throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
      }
      // 当 acquired 为 0 的时候就会回调设置的 ResourceListener 接口的 onResourceReleased() 方法
      if (--acquired == 0) {
        listener.onResourceReleased(key, this);
      }
    }
  }
}

前面说 Engine 也实现了 ResourceListener 接口,

// Engine.java
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
  activeResources.deactivate(cacheKey);
  if (resource.isCacheable()) {
    cache.put(cacheKey, resource);
  } else {
    resourceRecycler.recycle(resource);
  }
}

onResourceReleased() 方法中,就会将已经不是正在被使用的图片资源从 activeResources 中移除,然后根据缓存状态,将其转移至 LruResourceCache 中,或者回收释放。


3. 关于磁盘缓存

Glide 默认在磁盘中缓存的图片并不是原始的图片,而是经过一系列操作(如压缩、转换为目标高宽)转换来的(Resource)。当然也可以设置缓存原始图片(Source)。(因而两种图片对应的 key 值也是不同的)

DiskCacheStrategy.NONE: 表示不缓存任何内容。
DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。

磁盘缓存实现类由 InternalCacheDiskCacheFactory 创建,最终会通过缓存路径及缓存文件夹最大值创建一个 DiskLruCacheWrapper 对象。

DiskLruCacheWrapper 顾名思义是一个包装类,包装的是 DiskLruCache。且实现了 DiskCache 接口,该接口定义了磁盘缓存的操作。

另外,内部还持有 SafeKeyGenerator 对象,该对象可以根据 Key 对象得到对应的字符串 key 值,而字符串 key 就是用于索引磁盘中缓存的图片资源的。

以及,在向磁盘写入文件时(put 方法)会使用重入锁来同步代码,也就是 DiskCacheWriteLocker 类,其中主要是对 ReentrantLock 的包装。


参考文章
1、Android图片加载框架最全解析(三),深入探究Glide的缓存机制
2、Glide 源码分析解读-缓存模块-基于最新版Glide 4.9.0

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值