内存缓存
前引声明
- 上文中我们知道Glide内存缓存通过两部分组成又叫做运行时缓存
- LruCache -》
MemoryCache cache 实现类是LruResourceCache
在创建Glide的时候创建,大小根据当前手给应用分配的内存而定 具体请看源码里的MemorySizeCalculator方法
- 采用LruCache实现,遵循最近最少使用原则,当缓存大小达到设定缓存大小时将最近使用的最少的缓存对象清除
- 其内部维护了一个LinkedHashMap 将 缓存对象的强引用缓存
- 该缓存只存储当前没在使用的资源
- 弱引用缓存(活动缓存)-》
Map<Key, WeakReference<EngineResource<?>>> activeResources
- 是一个Map集合,存储资源的弱引用
- 该缓存存储的是正在使用的资源
- 这里为什么使用弱引用缓存
- 因为
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中没有正在使用的缓存对象
存
- 前面的文章分析知道
- 从磁盘和网络获取缓存是创建
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
里了
@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中