简单聊聊Glide的内存缓存
前言
glide的内存缓存有两级:LruCache、ActiveResources
其中LruCache老生常谈了,这里就不细说了。
ActiveResources实际上内含一个HashMap,Map中value则是资源的弱引用。
那么这两级是如何工作的?
取出
先从LruCache取,没有再从ActiveResources取
如果LruCache中有,则取出存入ActiveResources,并从LruCache移除
代码如下:
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
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();
//生成缓存的key
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
//从LruCache获取缓存图片
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);
}
...
}
存储
如果内存本地都没有,则从网络获取,获取后先存入ActiveResources,ActiveResources中存储的是EngineResource对象的弱引用。
EngineResource是将资源进行封装的一个类,它有一个计数acquired,记录资源被引用的次数,当资源被取出使用时+1(acquired函数),当资源被释放时-1(release函数)。当acquired为0时,会将它从ActiveResources中移除,存入LruCache。
代码如下:
void release() {
synchronized (listener) {
synchronized (this) {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
if (--acquired == 0) {
listener.onResourceReleased(key, this);
}
}
}
}
listener是Engine对象
@Override
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
可以看到如果开启内存缓存,则存入LruCache,否则直接释放。
两级缓存
这样我们就比较明白glide内存的两级缓存是怎么回事了,实际上是对缓存的资源进行了划分:使用中的和使用过的。
使用中的放入ActiveResources,这样可以防止被LruCache算法回收掉;而使用过的放在LruCache中,通过算法控制内存总量。
release何时执行
上面我们知道当资源被使用时会调用EngineResource的acquired函数,释放的时候会调用EngineResource的release函数。
使用的时候我们比较好理解,取出的时候其实就是使用的时候,这是一个主动的动作。
但是何时释放?glide中是怎么监控资源释放的?
通过查找EngineResource的release函数的调用,找到在Engine中
public void release(Resource<?> resource) {
if (resource instanceof EngineResource) {
((EngineResource<?>) resource).release();
} else {
throw new IllegalArgumentException("Cannot release anything but an EngineResource");
}
}
继续查找这个函数在哪里调用,发现在SingleRequest中
@Override
public synchronized void clear() {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
if (status == Status.CLEARED) {
return;
}
cancel();
// Resource must be released before canNotifyStatusChanged is called.
if (resource != null) {
releaseResource(resource);
}
if (canNotifyCleared()) {
target.onLoadCleared(getPlaceholderDrawable());
}
status = Status.CLEARED;
if (toRelease != null) {
engine.release(toRelease);
}
}
那么这个clear函数又在哪里调用?在ViewTarget中
@Synthetic void pauseMyRequest() {
Request request = getRequest();
// If the Request were cleared by the developer, it would be null here. The only way it's
// present is if the developer hasn't previously cleared this Target.
if (request != null) {
isClearedByUs = true;
request.clear();
isClearedByUs = false;
}
}
ViewTarget是对要加载图片的ImageView进行封装,而资源的释放也必然与View有关系。
ViewTarget有一个字段protected final T view;
这就是要加载图片的ImageView,另外在ViewTarget中可以看到对这个view添加了attach监听:
view.addOnAttachStateChangeListener(attachStateListener);
这个attachStateListener的源码:
attachStateListener = new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
resumeMyRequest();
}
@Override
public void onViewDetachedFromWindow(View v) {
pauseMyRequest();
}
};
这样就很明显了,每个加载图片的view都会注册一个OnAttachStateChangeListener,当这个view从界面移除的时候,也就是资源不再被引用的时候,就会调用pauseMyRequest,最终会将EngineResource的引用计数-1。
这样就保证了当ActiveResources中的资源不再被引用时,将这个资源转移到LruCache中。
相关文章
- 【技术种草】cdn+轻量服务器+hugo=让博客“云原生”一下
- CLB运维&运营最佳实践 ---访问日志大洞察
- vnc方式登陆服务器
- 轻松学排序算法:眼睛直观感受几种常用排序算法
- 十二个经典的大数据项目
- 为什么使用 CDN 内容分发网络?
- 大数据——大数据默认端口号列表
- Weld 1.1.5.Final,JSR-299 的框架
- JavaFX 2012:彻底开源
- 提升as3程序性能的十大要点
- 通过凸面几何学进行独立于边际的在线多类学习
- 利用行动影响的规律性和部分已知的模型进行离线强化学习
- ModelLight:基于模型的交通信号控制的元强化学习
- 浅谈Visual Source Safe项目分支
- 基于先验知识的递归卡尔曼滤波的代理人联合状态和输入估计
- 结合网络结构和非线性恢复来提高声誉评估的性能
- 最佳实践丨云开发CloudBase多环境管理实践
- TimeVAE:用于生成多变量时间序列的变异自动编码器
- 具有线性阈值激活的神经网络:结构和算法
- 内网渗透之横向移动 -- 从域外向域内进行密码喷洒攻击