Glide的缓存机制
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的缓存机制相关推荐
- Android图片加载框架最全解析(三),深入探究Glide的缓存机制
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/54895665 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭 ...
- Android 2020-2021年 最新面试题(附个人面试经历),Glide的缓存机制
4.Broadcast注册方式与区别 (1)静态注册(minifest),(2)动态注册 http://www.jianshu.com/p/ea5e233d9f43 此处延伸:什么情况下用动态注册 5 ...
- k8s组件的梳理,Glide的缓存机制
在打分阶段,调度器会为 Pod 从所有可调度节点中选取一个最合适的 Node.根据当前启用的打分规则,调度器会给每一个可调度节点进行打分. 最后,kube-scheduler 会将 Pod 调度到得分 ...
- Android面试:Glide的缓存机制,文末领取面试资料
程序员与别的专业有所不同,其他专业都是越老越香,而程序员却是一个例外,因为计算机技术更新太快,而且工作强度很大,因此大部分程序员只会写 3 年代码.3 年后要不晋升做项目经理,要么转行,个别研究所除外 ...
- Glide的缓存机制,大厂面试题汇总
作为一个3-5年的Android工程师,我们经常会遇到这些瓶颈: 1.技术视野窄 长期在小型软件公司,外包公司工作,技术视野被限制的太厉害 2.薪资提升难 初中级Android岗位薪资上升空间有限,基 ...
- Android Glide图片加载-缓存机制(内存缓存和磁盘缓存)
前言 glide的缓存机制.Glide的缓存设计是非常的先进的,考虑的场景也很周全.Glide 的缓存分为两种,一是内存缓存,另一个是硬盘缓存. 这两种缓存的作用各不相同,内存缓存的主要作用是防止应用 ...
- Glide 缓存机制解析(为啥使用弱引用)
目前图片框架,基本就是 Glide 一统江山了,除了极其简单的链式调用,里面丰富的 API 也让人爱不释手. 那么,这样一个好用的框架,里面的缓存机制是怎么样的呢? 我们知道,一般图片框架,加载图片, ...
- Android Glide图片加载框架(三)缓存机制
文章目录 一.缓存简介 二.缓存用法 内存缓存方式 磁盘缓存方式 三.缓存KEY 四.内存缓存 内存缓存流程 五.磁盘缓存 磁盘缓存流程 Android Glide图片加载框架系列文章 Android ...
- android glide设置缓存大小,Glide4-入门教程-5-缓存机制(内存缓存和磁盘缓存)
一.简介 这一节,主要是讲glide4的缓存机制.Glide的缓存设计是非常的先进的,考虑的场景也很周全.Glide 的缓存分为两种,一是内存缓存,另一个是硬盘缓存. 这两种缓存的作用各不相同,内存缓 ...
最新文章
- 网站打开速度变慢可以从哪些方面进行分析呢?
- ORA-14400: inserted partition key does not map to any partition
- [论文学习]Manifold Mixup和PatchUp的代码重新实现(实现即插即用且速度更快)
- java bean set_JavaBean自动生成get和set方法
- [转载]Sql Server 日期格式转换
- elon函数_看看Elon Musk的Hyperloop竞赛
- mysql 一对多约束条件_MySQL数据库/约束条件与表关系.md · 静谧之裳/python-learn - Gitee.com...
- [NOIP2014]自测
- 数据结构视频教程 严蔚敏
- Android安全测试框架Drozer(安装篇)
- win7 IE11下,无法通过Windows更新为其他微软产品获取更新
- Ingenuous Cubrency UVA - 11137 立方数之和 递推
- 微信 语音识别_微信语音识别_微信语音识别api - 云+社区 - 腾讯云
- js-函数式编程-柯里化和语义化
- html5我的心灵小屋,描写我的小屋优美句子
- 一个程序员单枪匹马,靠一个网站一年赚1个亿
- cgl证书(cgl证书查询官网)
- PyOpenGL代码实战(五):纹理
- python中pivot table 透视表实例
- 夺权!非结构化数据制霸大数据