Glide作为一个强大的图片加载框架,必然有着一套完整且优秀的缓存机制。本文将基于Glide-4.9.0版本,对Glide的缓存机制进行源码分析。
缓存相关的类
下面先介绍一下Glide缓存涉及到的一些类:
ActiveResources
ActiveResources是第一级缓存 ,表示当前正在活动的资源。当资源加载成功,或者通过其他缓存获得资源后都会将其添加到ActiveResources中。当资源被gc后,就会将其移除出ActiveResources。
ActiveResources通过Map来存储数据,数据保存在WeakReference中
1 2
final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
此外,还有一个引用队列
1
private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();
当一个弱引用对象被gc掉之后,其对应的Reference对象会加入到引用队列中。从引用队列获取到被gc掉的弱引用对象后,就可以将该对象从ActiveResources中删除。
MemoryCache
MemoryCache(内存缓存)是第二级缓存,如果ActiveResources没有获取到资源就从这里获取。
它的实现类是LruResourceCache,继承于LruCahce。主要的实现在LruCache中,LruCache使用LRU算法进行缓存。它的内部使用LinkedHashMap存储数据,LinkedHashMap内部可以设置为按照访问顺序进行排序,最近最少访问的在前面,这很适合LRU算法,在清除缓存的时候,只要从前面的元素开始删除,一直删除到满足容量即可。
LruCache
看下LruCache中存取数据的几个方法:
get方法获取指定数据
1 2 3 4
public synchronized Y (@NonNull T key) { return cache.get(key); }
remove方法删除指定数据
1 2 3 4 5 6 7 8 9
public synchronized Y remove (@NonNull T key) { final Y value = cache.remove(key); if (value != null ) { currentSize -= getSize(value); } return value; }
put方法添加缓存数据
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
public synchronized Y put (@NonNull T key, @Nullable Y item) { final int itemSize = getSize(item); if (itemSize >= maxSize) { onItemEvicted(key, item); return null ; } if (item != null ) { currentSize += itemSize; } final Y old = cache.put(key, item); if (old != null ) { currentSize -= getSize(old); if (!old.equals(item)) { onItemEvicted(key, old); } } evict(); return old; }
这几个方法还是比较好理解的,代码中都有注释。
MemorySizeCalculator
MemoryCache在创建Glide实例时初始化,内存缓存的初始化容量从MemorySizeCalculator中获得,代码如下:
1
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
getMemoryCacheSize方法如下:
1 2 3
public int getMemoryCacheSize () { return memoryCacheSize; }
其中memoryCacheSize在MemorySizeCalculator初始化时指定,MemorySizeCalculator的初始化也是发生在Glide实例创建时:
1 2 3
if (memorySizeCalculator == null ) { memorySizeCalculator = new MemorySizeCalculator.Builder(context).build(); }
这里采用了Builder模式,在其内部类Builder的build方法中调用了MemorySizeCalculator的构造方法:
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
MemorySizeCalculator(MemorySizeCalculator.Builder builder) { arrayPoolSize = isLowMemoryDevice(builder.activityManager) ? builder.arrayPoolSizeBytes / LOW_MEMORY_BYTE_ARRAY_POOL_DIVISOR : builder.arrayPoolSizeBytes; int maxSize = getMaxSize( builder.activityManager, builder.maxSizeMultiplier, builder.lowMemoryMaxSizeMultiplier); int widthPixels = builder.screenDimensions.getWidthPixels(); int heightPixels = builder.screenDimensions.getHeightPixels(); int screenSize = widthPixels * heightPixels * BYTES_PER_ARGB_8888_PIXEL; int targetBitmapPoolSize = Math.round(screenSize * builder.bitmapPoolScreens); int targetMemoryCacheSize = Math.round(screenSize * builder.memoryCacheScreens); int availableSize = maxSize - arrayPoolSize; if (targetMemoryCacheSize + targetBitmapPoolSize <= availableSize) { memoryCacheSize = targetMemoryCacheSize; bitmapPoolSize = targetBitmapPoolSize; } else { float part = availableSize / (builder.bitmapPoolScreens + builder.memoryCacheScreens); memoryCacheSize = Math.round(part * builder.memoryCacheScreens); bitmapPoolSize = Math.round(part * builder.bitmapPoolScreens); } }
可以看到,构造方法中指定了 BitmapPool 和 MemoryCache 可用的内存大小。 BitmapPool 是用来复用 Bitmap,从而避免重复创建 Bitmap 而带来的内存浪费。
小结一下计算BitmapPool和MemoryCache可用内存的步骤:
设置ArrayPool的容量,默认为4MB,低内存设备为2MB(安卓版本低于4.4默认为低内存,其它版本由系统判断)
设置最大容量,默认为当前进程可用内存 0.4,低内存设备 0.3
设置可用内存容量为最大容量减去ArrayPool的容量
计算一张大小为屏幕大小,格式为ARGB_8888的图片占用的内存大小(包括BitmapPool和MemoryCache),如果两者内存相加不超过可用容量,那么计算得出的内存大小即为各自的可用内存。如果相加后超出限制的话,两者按照比例平分可用内存。
过程分析
在执行Request的时候,会调用SingleRequest的onSizeReady方法进行加载数据:
SingleRequest#onSizeReady
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
@Override public synchronized void onSizeReady (int width, int height) { if (status != Status.WAITING_FOR_SIZE) { return ; } status = Status.RUNNING; loadStatus = engine.load( glideContext, model, requestOptions.getSignature(), this .width, this .height, requestOptions.getResourceClass(), transcodeClass, priority, requestOptions.getDiskCacheStrategy(), requestOptions.getTransformations(), requestOptions.isTransformationRequired(), requestOptions.isScaleOnlyOrNoTransform(), requestOptions.getOptions(), requestOptions.isMemoryCacheable(), requestOptions.getUseUnlimitedSourceGeneratorsPool(), requestOptions.getUseAnimationPool(), requestOptions.getOnlyRetrieveFromCache(), this , callbackExecutor); }
在真正加载资源前,将Request的状态变为RUNNING。之后调用Engine的load方法真正加载资源:
Engine#load
该方法首先生成缓存key,该key是一个EngineKey对象,由传入的多个参数生成。
1 2 3
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);
ActiveResources缓存
接着根据该缓存key获取缓存资源,首先从ActiveResources中获取缓存:
1 2 3 4 5 6 7 8
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); if (active != null ) { cb.onResourceReady(active, DataSource.MEMORY_CACHE); return null ; }
loadFromActiveResources方法:
1 2 3 4 5 6 7 8 9 10 11 12 13
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) { if (!isMemoryCacheable) { return null ; } EngineResource<?> active = activeResources.get(key); if (active != null ) { active.acquire(); } return active; }
内存缓存
如果ActiveResources中获取不到,就从内存中获取缓存,如果成功获取到缓存,就将缓存从内存中删除并将添加到ActiceResources中:
1 2 3 4 5 6 7 8
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); if (cached != null ) { cb.onResourceReady(cached, DataSource.MEMORY_CACHE); return null ; }
loadFromCache方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) { if (!isMemoryCacheable) { return null ; } EngineResource<?> cached = getEngineResourceFromCache(key); if (cached != null ) { cached.acquire(); activeResources.activate(key, cached); } return cached; }
磁盘缓存
如果在内存中获取不到缓存,就要执行以下步骤
1 2 3 4 5 6 7
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache); if (current != null ) { current.addCallback(cb, callbackExecutor); return new LoadStatus(cb, current); }
其中jobs为Jobs对象,看Jobs的get方法:
1 2 3
EngineJob<?> get(Key key, boolean onlyRetrieveFromCache) { return getJobMap(onlyRetrieveFromCache).get(key); }
继续看getJobMap方法:
1 2 3
private Map<Key, EngineJob<?>> getJobMap(boolean onlyRetrieveFromCache) { return onlyRetrieveFromCache ? onlyCacheJobs : jobs; }
其中:onlyCacheJobs和jobs的定义如下:
1 2
private final Map<Key, EngineJob<?>> jobs = new HashMap<>();private final Map<Key, EngineJob<?>> onlyCacheJobs = new HashMap<>();
最终,返回一个EngineJob对象,并且第一次加载的话返回null,继续执行下面语句
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
EngineJob<R> engineJob = engineJobFactory.build( key, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache); DecodeJob<R> decodeJob = decodeJobFactory.build( glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, onlyRetrieveFromCache, options, engineJob); jobs.put(key, engineJob); engineJob.addCallback(cb, callbackExecutor); engineJob.start(decodeJob); return new LoadStatus(cb, engineJob);
先创建EngineJob和DecodeJob,然后将EngineJob加到Jobs的Map中保存起来。EngineJob主要用于执行DecodeJob以及管理加载完成的回调,DecodeJob是一个Runnable,负责从磁盘或网络中加载数据。
EngineJob通过start方法执行DecodeJob
1 2 3 4 5 6 7
public synchronized void start (DecodeJob<R> decodeJob) { this .decodeJob = decodeJob; GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor(); executor.execute(decodeJob); }
可以看到,DecodeJob是在线程池里执行的,DecodeJob的run中又调用了runWrapped方法:
DecodeJob#runWrapped
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
private void runWrapped () { switch (runReason) { case INITIALIZE: stage = getNextStage(Stage.INITIALIZE); currentGenerator = getNextGenerator(); runGenerators(); break ; case SWITCH_TO_SOURCE_SERVICE: runGenerators(); break ; case DECODE_DATA: decodeFromRetrievedData(); break ; default : throw new IllegalStateException("Unrecognized run reason: " + runReason); } }
其中状态的变化如下:
DecodeJob#getNextStage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
private Stage getNextStage (Stage current) { switch (current) { case INITIALIZE: return diskCacheStrategy.decodeCachedResource() ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE); case RESOURCE_CACHE: return diskCacheStrategy.decodeCachedData() ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE); case DATA_CACHE: return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE; case SOURCE: case FINISHED: return Stage.FINISHED; default : throw new IllegalArgumentException("Unrecognized stage: " + current); } }
注意区分两种状态:在runWrapped方法中的状态为RunReason,在getNextStage方法中的状态为Stage。
在获取下一State时,调用DiskCacheStrategy对象的decodeCachedResource方法,用户可以通过设置相应的DiskCacheStrategy对象来配置Glide的硬盘缓存,用法如下:
1 2 3 4 5
Glide.with(MainActivity.this ) .applyDefaultRequestOptions(new RequestOptions() .diskCacheStrategy(DiskCacheStrategy.NONE)) .load(url) .into(mPicIv);
调用diskCacheStrategy方法并传入DiskCacheStrategy.NONE后,就可以禁止掉硬盘缓存。硬盘缓存策略共有以下几种:
DiskCacheStrategy.NONE:不缓存任何内容
DiskCacheStrategy.DATA:只缓存原始图片
DiskCacheStrategy.RESOURCE:只缓存转换后的图片
DiskCacheStrategy.ALL:既缓存原始图片,也缓存转换后的图片
DiskCacheStrategy.AUTOMATIC:让Glide根据图片资源智能选择策略(默认)
回到runWrapped方法,在确定了state后,根据state获得对应的DataFetcherGenerator,看getNextGenerator方法:
DecodeJob#getNextGenerator
1 2 3 4 5 6 7 8 9 10 11 12 13 14
private DataFetcherGenerator getNextGenerator () { switch (stage) { case RESOURCE_CACHE: return new ResourceCacheGenerator(decodeHelper, this ); case DATA_CACHE: return new DataCacheGenerator(decodeHelper, this ); case SOURCE: return new SourceGenerator(decodeHelper, this ); case FINISHED: return null ; default : throw new IllegalStateException("Unrecognized stage: " + stage); } }
获得相应DataFetcherGenerator后,执行runGenerators方法:
DecodeJob#runGenerators
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
private void runGenerators () { boolean isStarted = false ; while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) { stage = getNextStage(stage); currentGenerator = getNextGenerator(); if (stage == Stage.SOURCE) { reschedule(); return ; } } if ((stage == Stage.FINISHED || isCancelled) && !isStarted) { notifyFailed(); } }
首先会执行对应Generator的startNext方法,假如state为RESOURCE_CACHE(磁盘缓存策略为DiskCacheStrategy.RESOURCE或DiskCacheStrategy.ALL),则执行ResourceCacheGenerator的startNext方法:
ResourceCacheGenerator#startNext
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
public boolean startNext () { while (modelLoaders == null || !hasNextModelLoader()) { currentKey = new ResourceCacheKey( helper.getArrayPool(), sourceId, helper.getSignature(), helper.getWidth(), helper.getHeight(), transformation, resourceClass, helper.getOptions()); cacheFile = helper.getDiskCache().get(currentKey); if (cacheFile != null ) { sourceKey = sourceId; modelLoaders = helper.getModelLoaders(cacheFile); modelLoaderIndex = 0 ; } } loadData = null ; boolean started = false ; while (!started && hasNextModelLoader()) { ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++); loadData = modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions()); if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) { started = true ; loadData.fetcher.loadData(helper.getPriority(), this ); } } return started; }
在该方法中,如果能够获取到缓存文件,就先得到能够加载这个缓存文件的ModelLoaders,之后通过ModelLoader构造出对应的LoadData,最后通过该LoadData的DataFetcher的loadData方法加载缓存文件的数据。
假如这里调用的ByteBufferFetcher的loadData方法:
1 2 3 4 5 6 7 8 9 10 11 12 13
@Override public void loadData (@NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) { ByteBuffer result; try { result = ByteBufferUtil.fromFile(file); } catch (IOException e) { callback.onLoadFailed(e); return ; } callback.onDataReady(result); }
成功的话会回调onDataReady方法,将加载到的数据传出去,先存放在DecodeJob中,之后进行相关操作:
1 2 3 4 5 6 7 8 9
@Override public void onDataFetcherReady (Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) { this .currentData = data; }
而失败的话会回调onLoadFailed方法,也是在DecodeJob中处理
1 2 3 4 5 6 7 8 9 10 11 12
@Override public void onDataFetcherFailed (Key attemptedKey, Exception e, DataFetcher<?> fetcher, DataSource dataSource) { if (Thread.currentThread() != currentThread) { runReason = RunReason.SWITCH_TO_SOURCE_SERVICE; callback.reschedule(this ); } else { runGenerators(); } }
可以看到,在加载数据失败后不会就此结束,而是重新执行runGenerators方法再次尝试获取数据。
对于第二种情况,假如state为DATA_CACHE(磁盘缓存策略为DiskCacheStrategy.DATA或DiskCacheStrategy.ALL),则执行DataCacheGenerator的startNext方法,该方法的过程ResourceCacheGenerator类似,就不多说了。
现在看第三种情况,当state为SOURCE时,对应的Generator为SourceGenerator,同样调用SourceGenerator的startNext方法:
SourceGenerator#startNext
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
@Override public boolean startNext () { if (dataToCache != null ) { Object data = dataToCache; dataToCache = null ; cacheData(data); } if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) { return true ; } sourceCacheGenerator = null ; loadData = null ; boolean started = false ; while (!started && hasNextModelLoader()) { loadData = helper.getLoadData().get(loadDataListIndex++); if (loadData != null && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource()) || helper.hasLoadPath(loadData.fetcher.getDataClass()))) { started = true ; loadData.fetcher.loadData(helper.getPriority(), this ); } } return started; }
该方法中,如果已经获取到了数据,就将数据添加到磁盘缓存中,之后如果可以从磁盘缓存加载数据就不再执行后面操作。否则的话,同样是先通过相应的ModelLoader获得LoadData对象,然后通过LoadData中的DataFetcher的loadData方法加载数据(例如通过url从网络加载图片资源)。
如果加载数据成功,会先在SourceGenerator中回调onDataReady方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@Override public void onDataReady (Object data) { DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy(); if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) { dataToCache = data; cb.reschedule(); } else { cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher, loadData.fetcher.getDataSource(), originalKey); } }
加载成功时,会判断是否要缓存到磁盘(如果是从网络加载的资源,当磁盘缓存策略为DiskCacheStrategy.DATA和DiskCacheStrategy.ALL时,会缓存起来),要缓存到磁盘时,会重新进入startNext方法并将数据缓存到磁盘。如果不缓存到硬盘就将数据传递出去。
如果加载数据失败,同样会重新执行runGenerators方法再次尝试获取数据。
分析到这里,Engine的load方法算是分析完毕了,这个方法是真正用于加载数据的,通过该方法可以了解到Glide的缓存机制。这里小结一下Engine的load方法的执行步骤:
小结
Engine#load的执行步骤
生成缓存key:该key是一个EngineKey对象,由传入的多个参数生成。之后通过缓存key获取缓存资源
首先从ActiveResources中获取缓存,ActiveResources是第一级缓存,它通过HashMap来存储数据,数据保存在WeakReference中,此外还有一个引用队列,当某个数据的弱引用对象被gc掉之后,其对应的Reference对象会加入到引用队列中。从引用队列获取到被gc掉的弱引用对象后就可以将这些对象从ActiveResources中删除。
如果ActiveResources中获取不到,就从内存中获取,内存缓存是第二级缓存。内存缓存使用了LRU算法,内部使用LinkedHashMap存储数据。LinkedHashMap内部可以设置为按照访问顺序进行排序,最近最少访问的在前面,很适合LRU算法。如果成功获取到缓存,就将缓存从内存中删除并将添加到ActiceResources中。
如果内存中也获取不到缓存,就会创建EngineJob和DecodeJob,DecodeJob是一个Runnable。EngineJob将DecodeJob提交到线程池中执行。DecodeJob根据资源的磁盘缓存策略,是缓存原图还是缓存转换后的图片,从磁盘中获取不同的缓存文件,获取缓存文件成功后,先得到能够加载该缓存文件的ModelLoader,之后通过ModelLoader构造出对应的LoadData,最后通过该LoadData的DataFetcher加载缓存文件的数据。加载数据成功会将数据传递出去,失败的话会重新尝试获取缓存文件并加载数据。
如果不能从磁盘中得到缓存,会继续在DecodeJob中通过相应的ModelLoader获得LoadData对象并加载数据,不过这次是从数据源加载数据,例如从通过url从网络加载数据。加载数据成功后,会判断是否要缓存到磁盘要缓存到磁盘时,会重新进入startNext方法并将数据缓存到磁盘。如果不缓存到硬盘就将数据传递出去。加载数据失败的话会重新尝试加载,因为可能其他的ModelLoader可以加载成功,所以要不断尝试,直到所有ModelLoader都尝试完。
参考
近期评论