Glide的缓存分为两个模块,一个是内存缓存,一个是硬盘缓存。

内存缓存的作用是防止应用重复将图片数据读取到内存当中;

硬盘缓存的作用是防止应用重复从网络或其他地方下载和读取数据。

Glide的缓存Key

生成缓存Key的代码在Engine类的load()方法当中:

final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height,
loadProvider.getCacheDecoder(),loadProvider.getSourceDecoder(),
transformation, loadProvider.getEncoder(),transcoder,
loadProvider.getSourceEncoder());

fetcher.getId()方法获得了一个id字符串,这个字符串也就是我们要加载的图片的唯一标识。

如果是网络上的图片,那么这个id就是这张图片的url地址。

内存缓存

默认情况下,Glide自动就是开启内存缓存的。

若要禁用内存缓存:

Glide.with(this).load(url).skipMemoryCache(true).into(imageView);

Glide内存缓存是使用LruCache实现的

在GlideBuilder类中:

Glide createGlide() {if (sourceService == null) {final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());sourceService = new FifoPriorityThreadPoolExecutor(cores);}if (diskCacheService == null) {diskCacheService = new FifoPriorityThreadPoolExecutor(1);}MemorySizeCalculator calculator = new MemorySizeCalculator(context);if (bitmapPool == null) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {int size = calculator.getBitmapPoolSize();bitmapPool = new LruBitmapPool(size);} else {bitmapPool = new BitmapPoolAdapter();}}if (memoryCache == null) {memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());}if (diskCacheFactory == null) {diskCacheFactory = new InternalCacheDiskCacheFactory(context);}if (engine == null) {engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);}if (decodeFormat == null) {decodeFormat = DecodeFormat.DEFAULT;}return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}

第21行,创建了一个LruResourceCache实例,LruResourceCache继承了LruCache。

在Engine类的load()方法当中:

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();long startTime = LogTime.getLogTime();final String id = fetcher.getId();EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),transcoder, loadProvider.getSourceEncoder());EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);if (cached != null) {cb.onResourceReady(cached);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Loaded resource from cache", startTime, key);}return null;}EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);if (active != null) {cb.onResourceReady(active);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Loaded resource from active resources", startTime, key);}return null;}EngineJob current = jobs.get(key);if (current != null) {current.addCallback(cb);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Added to existing load", startTime, key);}return new LoadStatus(cb, current);}EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,transcoder, diskCacheProvider, diskCacheStrategy, priority);EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);jobs.put(key, engineJob);engineJob.addCallback(cb);engineJob.start(runnable);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Started new load", startTime, key);}return new LoadStatus(cb, engineJob);}

可以看到,这里在第12行调用了loadFromCache()方法来获取缓存图片,如果获取到就直接调用cb.onResourceReady()方法进行回调。如果没有获取到,则会在第21行调用loadFromActiveResources()方法来获取缓存图片,获取到的话也直接进行回调。只有在两个方法都没有获取到缓存的情况下,才会继续向下执行,从而开启线程来加载图片。

也就是说,Glide的图片加载过程中会调用两个方法来获取内存缓存,loadFromCache()和loadFromActiveResources()。这两个方法中一个使用的就是LruCache算法,另一个使用的就是弱引用。

在Engine类中:

private final MemoryCache cache;
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {if (!isMemoryCacheable) {return null;}EngineResource<?> cached = getEngineResourceFromCache(key);if (cached != null) {cached.acquire();activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));}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) {result = (EngineResource) cached;} else {result = new EngineResource(cached, true /*isCacheable*/);}return result;}private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {if (!isMemoryCacheable) {return null;}EngineResource<?> active = null;WeakReference<EngineResource<?>> activeRef = activeResources.get(key);if (activeRef != null) {active = activeRef.get();if (active != null) {active.acquire();} else {activeResources.remove(key);}}return active;}

在loadFromCache()方法中,调用了getEngineResourceFromCache()方法来获取缓存。在这个方法中,会使用缓存Key来从cache当中取值,而这里的cache对象就是在构建Glide对象时创建的LruResourceCache。

第16行,当我们从LruResourceCache中获取到缓存图片之后会将它从缓存中移除,然后在第10行将这个缓存图片存储到activeResources当中。activeResources就是一个弱引用的HashMap,用来缓存正在使用中的图片,我们可以看到,loadFromActiveResources()方法就是从activeResources这个HashMap当中取值的。使用activeResources来缓存正在使用中的图片,可以保护这些图片不会被LruCache算法回收掉。

总结一下,从内存缓存中读取数据的逻辑,如果能从内存缓存当中读取到要加载的图片,那么就直接进行回调,如果读取不到的话,才会开启线程执行后面的图片加载逻辑。

内存缓存的逻辑分析完了,接着分析内存缓存是在哪里写入的。

Engine的onEngineJobComplete()方法当中:

    @Overridepublic void onEngineJobComplete(Key key, EngineResource<?> resource) {Util.assertMainThread();// A null resource indicates that the load failed, usually due to an exception.if (resource != null) {resource.setResourceListener(key, this);if (resource.isCacheable()) {activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));}}jobs.remove(key);}

在第8行,回调过来的EngineResource被put到了activeResources当中,也就是在这里写入的缓存。那么这只是弱引用缓存,还有另外一种LruCache缓存是在哪里写入的呢?

EngineResource类中的引用机制,用一个acquired变量记录图片被引用的次数,调用acquire()方法会让变量加1,调用release()方法会让变量减1,代码如下所示:

private int acquired;void acquire() {if (isRecycled) {throw new IllegalStateException("Cannot acquire a recycled resource");}if (!Looper.getMainLooper().equals(Looper.myLooper())) {throw new IllegalThreadStateException("Must call acquire on the main thread");}++acquired;}void release() {if (acquired <= 0) {throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");}if (!Looper.getMainLooper().equals(Looper.myLooper())) {throw new IllegalThreadStateException("Must call release on the main thread");}if (--acquired == 0) {listener.onResourceReleased(key, this);}}

当acquired==0时,调用listener的onResourceReleased()方法来释放资源,listener是Engine对象,它的onResourceReleased()方法:

    @Overridepublic void onResourceReleased(Key cacheKey, EngineResource resource) {Util.assertMainThread();activeResources.remove(cacheKey);if (resource.isCacheable()) {cache.put(cacheKey, resource);} else {resourceRecycler.recycle(resource);}}

可以看到,这里首先会将缓存图片从activeResources中移除,然后再将它put到LruResourceCache当中。这样也就实现了正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存的功能。

总结:

1)先从LruCache中获取缓存,若没有则从弱引用中获取缓存;

2)异步加载图片完成后,缓存到弱引用;

3)从LruCache中获取缓存后,先移除,再缓存到弱引用;

4)资源释放时,先从弱引用中移除,再存入到LruCache中。

硬盘缓存

禁止硬盘缓存:

Glide.with(this).load(url).diskCacheStrategy(DiskCacheStrategy.NONE).into(imageView);

硬盘缓存的实现也是使用的LruCache算法,Glide使用的自己编写的DiskLruCache工具类,但是基本的实现原理都是差不多的。

Glide开启线程来加载图片,会执行EngineRunnable的run()方法,run()方法中又会调用一个decode()方法:

private Resource<?> decode() throws Exception {if (isDecodingFromCache()) {return decodeFromCache();} else {return decodeFromSource();}
}

可以看到,这里分为两种情况,一种是调用decodeFromCache()方法从硬盘缓存当中读取图片,一种是调用decodeFromSource()来读取原始图片。默认情况下Glide会优先从缓存当中读取,只有缓存中不存在要读取的图片时,才会去读取原始图片。decodeFromCache()方法的源码:

private Resource<?> decodeFromCache() throws Exception {Resource<?> result = null;try {result = decodeJob.decodeResultFromCache();} catch (Exception e) {if (Log.isLoggable(TAG, Log.DEBUG)) {Log.d(TAG, "Exception decoding result from cache: " + e);}}if (result == null) {result = decodeJob.decodeSourceFromCache();}return result;
}

可以看到,这里会先去调用DecodeJob的decodeResultFromCache()方法来获取缓存,如果获取不到,会再调用decodeSourceFromCache()方法获取缓存,这两个方法的区别其实就是DiskCacheStrategy.RESULT和DiskCacheStrategy.SOURCE这两个参数的区别。

public Resource<Z> decodeResultFromCache() throws Exception {if (!diskCacheStrategy.cacheResult()) {return null;}long startTime = LogTime.getLogTime();Resource<T> transformed = loadFromCache(resultKey);startTime = LogTime.getLogTime();Resource<Z> result = transcode(transformed);return result;
}public Resource<Z> decodeSourceFromCache() throws Exception {if (!diskCacheStrategy.cacheSource()) {return null;}long startTime = LogTime.getLogTime();Resource<T> decoded = loadFromCache(resultKey.getOriginalKey());return transformEncodeAndTranscode(decoded);
}

可以看到,它们都是调用了loadFromCache()方法从缓存当中读取数据,如果是decodeResultFromCache()方法就直接将数据解码并返回,如果是decodeSourceFromCache()方法,还要调用一下transformEncodeAndTranscode()方法先将数据转换一下再解码并返回。

需要注意,这两个方法中在调用loadFromCache()方法时传入的参数不一样,一个传入的是resultKey,另外一个却又调用了resultKey的getOriginalKey()方法。

接着看loadFromCache()方法的源码:

private Resource<T> loadFromCache(Key key) throws IOException {File cacheFile = diskCacheProvider.getDiskCache().get(key);if (cacheFile == null) {return null;}Resource<T> result = null;try {result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);} finally {if (result == null) {diskCacheProvider.getDiskCache().delete(key);}}return result;
}

调用getDiskCache()方法获取到的就是Glide自己编写的DiskLruCache工具类的实例,然后调用它的get()方法并把缓存Key传入,就能得到硬盘缓存的文件了。如果文件为空就返回null,如果文件不为空则将它解码成Resource对象后返回即可。

接着分析硬盘缓存是在哪里写入的。在没有缓存的情况下,会调用decodeFromSource()方法来读取原始图片。

public Resource<Z> decodeFromSource() throws Exception {Resource<T> decoded = decodeSource();return transformEncodeAndTranscode(decoded);
}

decodeSource()是用来解析原图片的,而transformEncodeAndTranscode()则是用来对图片进行转换和转码的。先看decodeSource()方法:

private Resource<T> decodeSource() throws Exception {Resource<T> decoded = null;try {long startTime = LogTime.getLogTime();final A data = fetcher.loadData(priority);if (isCancelled) {return null;}decoded = decodeFromSourceData(data);} finally {fetcher.cleanup();}return decoded;
}private Resource<T> decodeFromSourceData(A data) throws IOException {final Resource<T> decoded;if (diskCacheStrategy.cacheSource()) {decoded = cacheAndDecodeSourceData(data);} else {long startTime = LogTime.getLogTime();decoded = loadProvider.getSourceDecoder().decode(data, width, height);}return decoded;
}private Resource<T> cacheAndDecodeSourceData(A data) throws IOException {long startTime = LogTime.getLogTime();SourceWriter<A> writer = new SourceWriter<A>(loadProvider.getSourceEncoder(), data);diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);startTime = LogTime.getLogTime();Resource<T> result = loadFromCache(resultKey.getOriginalKey());return result;
}

在第5行先调用fetcher的loadData()方法读取图片数据,然后在第9行调用decodeFromSourceData()方法来对图片进行解码。

接下来会在第18行先判断是否允许缓存原始图片,如果允许的话又会调用cacheAndDecodeSourceData()方法。而在这个方法中同样调用了getDiskCache()方法来获取DiskLruCache实例,接着调用它的put()方法就可以写入硬盘缓存了,注意原始图片的缓存Key是用的resultKey.getOriginalKey()。

接下来分析一下transformEncodeAndTranscode()方法的源码,来看看转换过后的图片缓存是怎么写入的:

private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {long startTime = LogTime.getLogTime();Resource<T> transformed = transform(decoded);writeTransformedToCache(transformed);startTime = LogTime.getLogTime();Resource<Z> result = transcode(transformed);return result;
}private void writeTransformedToCache(Resource<T> transformed) {if (transformed == null || !diskCacheStrategy.cacheResult()) {return;}long startTime = LogTime.getLogTime();SourceWriter<Resource<T>> writer = new SourceWriter<Resource<T>>(loadProvider.getEncoder(), transformed);diskCacheProvider.getDiskCache().put(resultKey, writer);
}

先在第3行调用transform()方法来对图片进行转换,然后在writeTransformedToCache()方法中将转换过后的图片写入到硬盘缓存中,调用的同样是DiskLruCache实例的put()方法,不过这里用的缓存Key是resultKey。

总结:

1)从硬盘缓存中获取,先获取处理后的图片,获取不到再获取原图

2)没有缓存时,会去获取原图,下载原图后,若允许缓存原图(一般不会缓存原图),会先缓存原图,接着处理图片后,再缓存处理后图片。

到这里,硬盘缓存的实现原理就分析完了。

Glide的缓存机制相关推荐

  1. Android图片加载框架最全解析(三),深入探究Glide的缓存机制

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/54895665 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭 ...

  2. Android 2020-2021年 最新面试题(附个人面试经历),Glide的缓存机制

    4.Broadcast注册方式与区别 (1)静态注册(minifest),(2)动态注册 http://www.jianshu.com/p/ea5e233d9f43 此处延伸:什么情况下用动态注册 5 ...

  3. k8s组件的梳理,Glide的缓存机制

    在打分阶段,调度器会为 Pod 从所有可调度节点中选取一个最合适的 Node.根据当前启用的打分规则,调度器会给每一个可调度节点进行打分. 最后,kube-scheduler 会将 Pod 调度到得分 ...

  4. Android面试:Glide的缓存机制,文末领取面试资料

    程序员与别的专业有所不同,其他专业都是越老越香,而程序员却是一个例外,因为计算机技术更新太快,而且工作强度很大,因此大部分程序员只会写 3 年代码.3 年后要不晋升做项目经理,要么转行,个别研究所除外 ...

  5. Glide的缓存机制,大厂面试题汇总

    作为一个3-5年的Android工程师,我们经常会遇到这些瓶颈: 1.技术视野窄 长期在小型软件公司,外包公司工作,技术视野被限制的太厉害 2.薪资提升难 初中级Android岗位薪资上升空间有限,基 ...

  6. Android Glide图片加载-缓存机制(内存缓存和磁盘缓存)

    前言 glide的缓存机制.Glide的缓存设计是非常的先进的,考虑的场景也很周全.Glide 的缓存分为两种,一是内存缓存,另一个是硬盘缓存. 这两种缓存的作用各不相同,内存缓存的主要作用是防止应用 ...

  7. Glide 缓存机制解析(为啥使用弱引用)

    目前图片框架,基本就是 Glide 一统江山了,除了极其简单的链式调用,里面丰富的 API 也让人爱不释手. 那么,这样一个好用的框架,里面的缓存机制是怎么样的呢? 我们知道,一般图片框架,加载图片, ...

  8. Android Glide图片加载框架(三)缓存机制

    文章目录 一.缓存简介 二.缓存用法 内存缓存方式 磁盘缓存方式 三.缓存KEY 四.内存缓存 内存缓存流程 五.磁盘缓存 磁盘缓存流程 Android Glide图片加载框架系列文章 Android ...

  9. android glide设置缓存大小,Glide4-入门教程-5-缓存机制(内存缓存和磁盘缓存)

    一.简介 这一节,主要是讲glide4的缓存机制.Glide的缓存设计是非常的先进的,考虑的场景也很周全.Glide 的缓存分为两种,一是内存缓存,另一个是硬盘缓存. 这两种缓存的作用各不相同,内存缓 ...

最新文章

  1. 网站打开速度变慢可以从哪些方面进行分析呢?
  2. ORA-14400: inserted partition key does not map to any partition
  3. [论文学习]Manifold Mixup和PatchUp的代码重新实现(实现即插即用且速度更快)
  4. java bean set_JavaBean自动生成get和set方法
  5. [转载]Sql Server 日期格式转换
  6. elon函数_看看Elon Musk的Hyperloop竞赛
  7. mysql 一对多约束条件_MySQL数据库/约束条件与表关系.md · 静谧之裳/python-learn - Gitee.com...
  8. [NOIP2014]自测
  9. 数据结构视频教程 严蔚敏
  10. Android安全测试框架Drozer(安装篇)
  11. win7 IE11下,无法通过Windows更新为其他微软产品获取更新
  12. Ingenuous Cubrency UVA - 11137 立方数之和 递推
  13. 微信 语音识别_微信语音识别_微信语音识别api - 云+社区 - 腾讯云
  14. js-函数式编程-柯里化和语义化
  15. html5我的心灵小屋,描写我的小屋优美句子
  16. 一个程序员单枪匹马,靠一个网站一年赚1个亿
  17. cgl证书(cgl证书查询官网)
  18. PyOpenGL代码实战(五):纹理
  19. python中pivot table 透视表实例
  20. 夺权!非结构化数据制霸大数据

热门文章

  1. Oracle根据日期区间查询Date类型的数据
  2. 使用JPA进行Update操作 @Query注解的用法,JPL
  3. 难忘的一天——装操作系统(一)
  4. 相关性检验_相关系数
  5. dataframe构建
  6. [Pytorch]基于混和精度的模型加速
  7. Hexo集成Algolia实现搜索功能
  8. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)
  9. 特斯拉超级计算机Dojo
  10. TVM将深度学习模型编译为WebGL