内存缓存
前引声明
- 上文中我们知道Glide内存缓存通过两部分组成又叫做运行时缓存
- LruCache -》
MemoryCache cache 实现类是LruResourceCache
在创建Glide的时候创建,大小根据当前手给应用分配的内存而定 具体请看源码里的MemorySizeCalculator方法- 采用LruCache实现,遵循最近最少使用原则,当缓存大小达到设定缓存大小时将最近使用的最少的缓存对象清除
- 其内部维护了一个LinkedHashMap 将 缓存对象的强引用缓存
- 该缓存只存储当前没在使用的资源
- 采用LruCache实现,遵循最近最少使用原则,当缓存大小达到设定缓存大小时将最近使用的最少的缓存对象清除
- 弱引用缓存(活动缓存)-》
Map<Key, WeakReference<EngineResource<?>>> activeResources
- 是一个Map集合,存储资源的弱引用
- 该缓存存储的是正在使用的资源
- 这里为什么使用弱引用缓存
- 因为
LruCache
的大小有限为了为了保护正在使用的对象不被lruCache
回收掉 - 为了不影响系统对无引用对象资源的回收
- 因为
- 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(); //获取id字符转 加载图片的唯一标识 final String id = fetcher.getId(); //生成缓存的key EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(), loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(), transcoder, loadProvider.getSourceEncoder()); // 第一处 1 .根据key 获取 缓存 EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); // 此时得到缓存 若不为空则调用ResourceCallback.onResourceReady 将缓存回调出去 if (cached != null) { cb.onResourceReady(cached); return null; } //第二处 2. 从弱引用缓存中获取 EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); // 不为空 if (active != null) { // 调用onResourceReady 给target 设置图片 cb.onResourceReady(active); return null; } //第三处 3. 若缓存中没有则开启新线程 创建 EngineJob 来从磁盘或者网络里加载 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); }
-
注意观察上面代码 三处涉及缓存
- 第一处
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
从Lrucache获取 - 第二处
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
从弱引用缓存获取 - 第三处
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
创建EngineRunnable 异步从磁盘缓存或者网络加载资源
- 第一处
-
下面看第一处
loadFromCache
以及其内部执行逻辑private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) { // 如果没使用缓存在返回null if (!isMemoryCacheable) { return null; } // 1. 根据key获取缓存 EngineResource<?> cached = getEngineResourceFromCache(key); if (cached != null) { // 2. 累计引用次数+1 cached.acquire(); // 3. 将其添加到弱引用缓存 key 为EngineKey value 为 ResourceWeakReference activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue())); } // 返回缓存 return cached; } //-> 在 1 处 调用 getEngineResourceFromCache(key);获取缓存 private EngineResource<?> getEngineResourceFromCache(Key key) { // 从缓存中通过remove key对应的缓存 来获取缓存 // 获取到说明要使用该资源 将资源从内存缓存中移除 // remove 方法 移除并返回当前key 对应的值 Resource<?> cached = cache.remove(key); //包装成EngineResource 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; }
- 上面可以看到 在
loadFromCache 1 的位置
通过 getEngineResourceFromCache
中通过remove(key)
方法移除key
所对应的缓存并得到该缓存,- 此时
lruCache
中已经将该key对应的缓存移除,代码执行返回 ,此时继续执行loadFromCache方法里的 2 位置的代码
- 在
2
位置代码判断当前返回缓存是否为空- 若不为空则调用
cached.acquire();
让该资源的被引用次数+1
,并put
到弱引用缓存activeResources
中去. 返回缓存 - 若为空不处理 直接返回 该缓存 此时代码执行到
Engine.load
中的第二处
通过loadFromActiveResources
方法直接从弱引用缓存中获取缓存- 在
第二处
之前判断从内存中得到的缓存是否为空- 若为空则调用第二处代码从弱引用缓存获取
- 不为空则直接通过回调函数
onResourceReady(cache)
通知target
设置图片
- 在
- 若不为空则调用
- 上面可以看到 在
-
下面看 第二处
loadFromActiveResources
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) { EngineResource<?> active = null; // 从弱引用缓存中获取EngineResource WeakReference<EngineResource<?>> activeRef = activeResources.get(key); if (activeRef != null) { active = activeRef.get(); //判断缓存是否为空 if (active != null) { // 引用次数+1 active.acquire(); } else { // 为空则移除该key activeResources.remove(key); } } return active; }
- 上面代码可知
- 直接从弱引用缓存
activeResources
获取缓存 - 判断是否为空
- 若为空则从
activeResources
中移除当前key
- 不为空则调用
acquire()
将该资源引用次数+1
- 若为空则从
- 执行return 该方法执行结束 此时代码执行到
Engine.load
中的第三处- 同样的在
第三处
之前判断从弱引用缓存得到的缓存是否为空- 不为空则直接通过回调函数
onResourceReady(cache)
通知target
设置图片 - 若为空则调用第三处代码从磁盘缓存,或者网络获取
- 不为空则直接通过回调函数
- 同样的在
- 直接从弱引用缓存
- 上面代码可知
-
当Lrucache和弱引用缓存中都没有时应该从磁盘或者网络中获取
- 在第一处 和第二处 都是从内存取缓存的过程
- 到了第三处则是缓存对象如何存到内存的过程了
取总结
- 先从LruCache中通过remove(key)获取缓存
- 若有则从LruCache中移除,
acquire+1
添加到弱引用缓存activeResources
中,并返回直接使用缓存 - 若没有就从弱引用缓存
activeResources
中获取- 若有则
acquire+1
并返回直接使用缓存 - 若没有则从磁盘或者网络获取
- 若有则
- 这里为什么先从LruCache中获取
- 为了保证LruCache中没有正在使用的缓存对象
- 若有则从LruCache中移除,
存
-
前面的文章分析知道
-
从磁盘和网络获取缓存是创建
EngineRunnable
进行异步加载的 -
具体加载方法在
run方法中的decode方法
加载到的resource -
当资源加载结束后根据resource的值是否为空调用了如下方法,这里以加载成功为例
-
if (resource == null) { onLoadFailed(exception);//加载失败 } else { onLoadComplete(resource);//加载成功 }
-
-
调用
onLoadComplete
之后通过Handler 将 发送message 通知主线程加载完成 -
在Handler的CallBack中 调用
job.handleResultOnMainThread();
处理加载完成的数据 -
private void handleResultOnMainThread() { //... //构建一个 EngineResource 对象 engineResource = engineResourceFactory.build(resource, isCacheable); hasResource = true; // 1. 刚加载完成肯定在使用,所以使用次数 +1 直接加入弱引用缓存 engineResource.acquire(); listener.onEngineJobComplete(key, engineResource); // 2. 调用ResourceCallback.onResourceReady 将engineResource通知给target,target接收到之后设置到view上 for (ResourceCallback cb : cbs) { if (!isInIgnoredCallbacks(cb)) { engineResource.acquire(); cb.onResourceReady(engineResource); } } // 3. 此时请求执行结束了,应该释放资源 engineResource.release(); }
-
1 ,2
,位置代码- 当我们刚加载完成,通知给
target
的时候资源正在被使用,所以使用次数+1
并put到弱引用缓存
中
- 当我们刚加载完成,通知给
-
3
位置代码 当请求执行结束 target 也操作结束,应该调用release
释放该资源-
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
的时候调用onResourceReleased
- –acquired(引用累计次数)==0 说明当前已经没有引用了 可以放到
LruCache
里了
- –acquired(引用累计次数)==0 说明当前已经没有引用了 可以放到
-
@Override public void onResourceReleased(Key cacheKey, EngineResource resource) { Util.assertMainThread(); // 1 从弱引用缓存中移除 activeResources.remove(cacheKey); // 2 判断是否可缓存 若可缓存则put到LruCache 若不可缓存则根据不同的资源类型调用不同的释放方法 若resource为bitmap则调用bitmap.recycle if (resource.isCacheable()) { cache.put(cacheKey, resource); } else { resourceRecycler.recycle(resource); } }
-
-
-
根据上面可以得出
- 当
acquired(引用累计次数)
>0的时候,放入弱引用缓存acquired>0
说明图片资源正在被使用
- 当
acquired(引用累计次数)==0
的时候 则从弱引用缓存移除放入Lrucache中acquired==0
说明图片资源没有被使用
- 当
-
存总结
- 资源加载完成
- 首先添加到弱引用缓存
acquired+1
,通知目标target 加载完成 - 当加载完成target操作结束后标记此次请求加载过程结束后要释放资源
- 判断
--acquired是否等于0
若为0 则从弱引用缓存中移除当前缓存,并put到LruCache中