阅读 265

Glide的内存缓存(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中。

 伪原创工具 SEO网站优化  https://www.237it.com/


作者:BennuCTech
链接:https://juejin.cn/post/7035496481071530020

文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐