Glide 缓存总结(一)

时间:2022-07-22
本文章向大家介绍Glide 缓存总结(一),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
Glide的缓存机制

主要分为2种缓存,一种是内存缓存,一种是磁盘缓存

三级缓存原理

加载一张图片的时候,获取顺序:

Lru算法缓存

【--->】

弱引用缓存

【--->】

磁盘缓存

源码解析如下:

public class Engine implements EngineJobListener,
       MemoryCache.ResourceRemovedListener,
       EngineResource.ResourceListener {
   
   public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
           DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
           Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
       Util.assertMainThread();

       //1.使用 LRU 机制从内存获取,成功则返回,获取不到,下一步
       EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
     
       //2.使用弱引用机制从内存获取,成功则返回,获取不到,下一步
       EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);

       //创建一个 runnable 下一步提交给线程池使用,run()方法就是线程池要执行的东东
       EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
       engineJob.start(runnable);

       if (Log.isLoggable(TAG, Log.VERBOSE)) {
           logWithTimeAndKey("Started new load", startTime, key);
      }
       return new LoadStatus(cb, engineJob);
  }    
}
// runnable
class EngineRunnable implements Runnable, Prioritized {
   

   @Override
   public void run() {
       try {
           //执行 decode()方法
           resource = decode();
      } catch (Exception e) {
           //...
      }
       //...
  }
   private Resource<?> decode() throws Exception {
       if (isDecodingFromCache()) {
           //3.从本地缓存获取
           return decodeFromCache();
      } else {
           //4. 从网络请求
           return decodeFromSource();
      }
  }    
}        
图片存储原理

从网络获取加载图片成功 存储的顺序是:

网络加载

【--->】

磁盘缓存

【--->】

弱引用缓存

【--->】

Lru算法缓存

首先从网络加载图片

若设置缓存原始图片

【1.1】磁盘存储 ,否则跳过这一步

没有设置则

【1.2】存储转换过的图片

【2】图片显示成功,立即放入弱引用缓存

【3】 比如页面退出,则将当前页面的图片数据从弱引用缓存移除,放如LRU缓存

class DecodeJob<A, T, Z> {
   
   private Resource<T> decodeSource() throws Exception {
       Resource<T> decoded = null;
       try {
           // 这是网络请求图片
           final A data = fetcher.loadData(priority);
           //【1.1】磁盘缓存 这儿是可选的: 存储图片的原始数据,没有处理过的
           //下一步1.2】
           decoded = decodeFromSourceData(data);
      } finally {
      }
       return decoded;
  }
   
   private void writeTransformedToCache(Resource<T> transformed) {
       SourceWriter<Resource<T>> writer = new SourceWriter<Resource<T>>(loadProvider.getEncoder(), transformed);
       // 【1.2】 磁盘缓存 这是存储转换过的图片数据
       //下一步【2】
       diskCacheProvider.getDiskCache().put(resultKey, writer);
  }    
}
public class Engine implements EngineJobListener,
       MemoryCache.ResourceRemovedListener,
       EngineResource.ResourceListener {
   
   @Override
   public void onEngineJobComplete(Key key, EngineResource<?> resource) {
       if (resource != null) {

           if (resource.isCacheable()) {
               //【2】即写入了弱引用 内存缓存
                //下一步【3】
               activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
          }
      }
  }  
   
   //【3】
   @Override
   public void onResourceReleased(Key cacheKey, EngineResource resource) {
       // 步骤1:将缓存图片从弱引用缓存中移除
       activeResources.remove(cacheKey);
       if (resource.isCacheable()) {
           // 步骤2:将该图片缓存放入Lru缓存
           cache.put(cacheKey, resource);
      }
  }    
}
弱引用缓存缓存GC回收原理

细心的会发现我们上面使用的弱引用缓存,其实就是一个 hashMap 对象,只要图片加载成功就会放入这个 map 缓存,那么 map里的缓存对象是怎么维护的?

答案就是:

ReferenceQueue+IdleHandler

ReferenceQueue作用:

ReferenceQueue可翻译为引用队列,换言之就是存放引用的队列,保存的是Reference对象。

其作用在于Reference对象所引用的对象被GC回收时,该Reference对象将会被加入引用队列(ReferenceQueue)中

IdleHandler 原理:

略...

在消息队列MessageQueue空闲的时候,会执行IdleHandler,

清除ReferenceQueue中清空ResourceWeakReference对象

public class Engine implements EngineJobListener,
       MemoryCache.ResourceRemovedListener,
       EngineResource.ResourceListener {
// 这是map 是一个强引用对象    
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;    
   
   private ReferenceQueue<EngineResource<?>> getReferenceQueue() {
       if (resourceReferenceQueue == null) {
           resourceReferenceQueue = new ReferenceQueue<EngineResource<?>>();
           MessageQueue queue = Looper.myQueue();
           queue.addIdleHandler(new RefQueueIdleHandler(activeResources, resourceReferenceQueue));
      }
       return resourceReferenceQueue;
  }
   //IdleHandler
   private static class RefQueueIdleHandler implements MessageQueue.IdleHandler {
       private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
       private final ReferenceQueue<EngineResource<?>> queue;

       public RefQueueIdleHandler(Map<Key, WeakReference<EngineResource<?>>> activeResources,
               ReferenceQueue<EngineResource<?>> queue) {
           this.activeResources = activeResources;
           this.queue = queue;
      }

       @Override
       public boolean queueIdle() {
           //从队列中取出一个元素,队列为空则返回null
           //不会空说明ReferenceQueue有被GC回收的ResourceWeakReference对象.
           ResourceWeakReference ref = (ResourceWeakReference) queue.poll();
           if (ref != null) {
               //清空 map 中 被GC回收的ResourceWeakReference对象
               activeResources.remove(ref.key);
          }

           return true;
      }
  }
       
}
Glide 用到几个线程池

2个

一个线程池负责从本地获取图片数据

一个线程池负责从网络获取图片数据

class EngineJob implements EngineRunnable.EngineRunnableManager {
   
   private final ExecutorService diskCacheService;
   private final ExecutorService sourceService;    
}

一张图片加载的流程

Glide 传入 url

【--UI线程->】从内存获取 ,成功则返回

【--本地线程池子线程->】从本地获取,转换的图片数据,成功则返回

【--本地线程池子线程->】从本地获取,原始图片数据,成功则返回

【--网络线程池子线程->】根据 url 网络加载,失败 显示 error 图片

【--网络线程池子线程->】若设置源数据存储 磁盘存储原始图片数据,然后解码, 否则跳过这一步

【--网络线程池子线程->】直接解码

【--网络线程池子线程->】解码图片数据转换,本地存储转换的图片数据,

   (所谓转换就是图片处理,缩放,圆角等等操作),使用 handler 切换线程

【--UI线程->】 弱引用缓存存储图片数据

【--UI线程->】 图片显示,动画巴拉巴拉的执行...

其他

IdleHandler 使用

见相关文章

更多内容 欢迎关注公众号