内存缓存
前引声明
- 上文中我们知道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设置图片
- 在
- 若不为空则调用
- 上面可以看到 在
-
下面看 第二处
loadFromActiveResourcesprivate 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中
本文深入探讨了Glide的内存缓存机制,包括LruCache和弱引用缓存activeResources的使用。在加载图片时,首先尝试从LruCache中移除并使用缓存,接着检查弱引用缓存。若两者都未找到,则从磁盘或网络加载资源。加载完成后,资源会被添加到弱引用缓存,并在不再使用时释放回LruCache。整个过程确保了内存的有效管理和资源的高效利用。
370

被折叠的 条评论
为什么被折叠?



