1.前言 Glide框架的核心内容主要包含生命周期自动管理和内存管理,生命周期管理在《Glide源码分析-生命周期管理 》有详细叙述到,本篇主要叙述Glide框架的内存管理。
2.内存管理总体框架 Glide内存管理分为如下图中五种情况,下面我们一一来分析这五种情况资源是如何在内存中移动的。
2.1 资源在活动内存中 1 2 3 4 5 6 7 8 final class ActiveResources { private final boolean isActiveResourceRetentionAllowed; private final Executor monitorClearedResourcesExecutor; @VisibleForTesting final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>(); private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>(); ...... }
第一种情况是资源仅在活动内存中。 Map<Key, ResourceWeakReference>就是我们所讲的活动内存,此内存保存界面正在加载的资源,当资源仅存在活动内存的时候,Glide会直接加载资源到控件中。
2.2 资源在内存缓存中 第二种情况是资源不在活动内存中,在内存缓存中。 从图中可以看出,这种情况资源会从内存缓存中删除(删除后如虚线圈所示 ),然后被挪到活动缓存中,Glide从活动缓存中加载资源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 private EngineResource<?> loadFromCache(Key key) { EngineResource<?> cached = getEngineResourceFromCache(key); if (cached != null ) { cached.acquire(); activeResources.activate(key, cached); } return cached; } private EngineResource<?> getEngineResourceFromCache(Key key) { Resource<?> cached = cache.remove(key); final EngineResource<?> result; if (cached == null ) { result = null ; } else if (cached instanceof EngineResource) { result = (EngineResource<?>) cached; } else { result = new EngineResource<>( cached, true , true , key, this ); } return result; } synchronized void activate (Key key, EngineResource<?> resource) { ResourceWeakReference toPut = new ResourceWeakReference( key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed); ResourceWeakReference removed = activeEngineResources.put(key, toPut); if (removed != null ) { removed.reset(); } }
2.3 资源在用户申明的Glide磁盘中 第三种情况是活动缓存和内存缓存都没有资源,资源保存在用户申明的磁盘缓存中。 这种情况Glide会尝试从本地磁盘缓存中获取,如果获取到了复制 资源(**实线圈)**到活动缓存中显示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 public <R> LoadStatus load ( GlideContext glideContext, Object model, Key signature, int width, int height, Class<?> resourceClass, Class<R> transcodeClass, Priority priority, DiskCacheStrategy diskCacheStrategy, Map<Class<?>, Transformation<?>> transformations, boolean isTransformationRequired, boolean isScaleOnlyOrNoTransform, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, boolean useAnimationPool, boolean onlyRetrieveFromCache, ResourceCallback cb, Executor callbackExecutor) { long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0 ; EngineKey key = keyFactory.buildKey( model, signature, width, height, transformations, resourceClass, transcodeClass, options); EngineResource<?> memoryResource; synchronized (this ) { memoryResource = loadFromMemory(key, isMemoryCacheable, startTime); if (memoryResource == null ) { return waitForExistingOrStartNewJob( glideContext, model, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, options, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache, cb, callbackExecutor, key, startTime); } } cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE); return null ; }
具体如何从磁盘缓存中获取资源流程可以参考《Glide源码分析 —4.2 从不同缓存中搜寻资源》
2.4 资源在云端 第四种情况本地没有资源,资源在云端。 这种情况Glide会首先搜寻活动缓存,然后搜寻内存缓存,再搜寻本地磁盘缓存。如果以上三者都搜不到,那就启动httpurlconnection从网络上加载资源。当资源下载好之后将资源保存在磁盘缓存中,并且将资源复制到活动缓存中,供界面显示。
2.5 资源回收 当空白Fragment感知到界面被销毁,onDestory()方法被调用之后,Glide最终会调用活动缓存中的onResourceReleased方法,此方法中资源会从活动缓存中回收,然后被保存到内存缓存中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void onResourceReleased (Key cacheKey, EngineResource<?> resource) { activeResources.deactivate(cacheKey); if (resource.isMemoryCacheable()) { cache.put(cacheKey, resource); } else { resourceRecycler.recycle(resource, false ); } } synchronized void deactivate (Key key) { ResourceWeakReference removed = activeEngineResources.remove(key); if (removed != null ) { removed.reset(); } }
3.在运行时缓存中为什么会做成两级缓存,意义何在 3.1 只有LRU内存缓存,没有活动缓存 LRU内存缓存有个特性,就是当LRU缓存装满的时候,如果还有资源需要存储,缓存会将最近最少使用的资源删除,然后保存新添加的资源。
如果只有LRU内存缓存的话,会出现一个BUG,接入我设置LRU内存缓存大小为5,如果RecycleView需要加载的资源为5张图片了,缓慢滚动屏幕,有一张图片出现,需要新加入到LRU中,那最先放入LRU中的那种图片就会被销毁,然而那种图片还有一部分正在显示。这样就会引起崩溃。
所以鉴于此BUG,活动内存就应运而生,活动内存是一个map集合,随便存多少,因此为了清晰管理内存。活动内存专门存放当前界面显示的资源,LRU缓存负责缓存暂时不在显示的资源。
3.2 只有活动缓存,没有LRU内存缓存 只有活动缓存(Map)不是不行,只是如果存储和删除都在一个map集合中,管理起来比较混乱,而且在操作map的时候一定要采用同步,扩大了同步操作范围,降低效率。
1 2 3 4 5 6 7 8 9 10 public void onResourceReleased (Key cacheKey, EngineResource<?> resource) { activeResources.deactivate(cacheKey); if (resource.isMemoryCacheable()) { cache.put(cacheKey, resource); } else { resourceRecycler.recycle(resource, false ); } }