当执行 Glide.with(this).load("").into(imageview)的into方法时候,该方法接收一个Target或者一个ImageView,Target是一个接口,默认的实现类包括SimpleTarget,ViewTarget,ImageViewTarget等,如果传递的是ImageView接下来回执行以下几步:
into - RequestBuilder - target/iamgeview - buildRequest - obtainRequest - SingleRequest - begin - onSizeReady - Engine
public LoadStatus load(…………………………省略参数…………………………) {
//开始加载的时间毫秒值
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
//图片缓存的key,从这里可以看出影响key的参数非常多
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的缓存分为三级缓存:弱引用内存缓存,LurCatch内存缓存,磁盘缓存,从上面可以看出内存缓存的方法是loadFromMemory,磁盘缓存的方法是waitForExistingOrStartNewJob。
private EngineResource> loadFromMemory(EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
//弱引用内存缓存
EngineResource> active = loadFromActiveResources(key);
if (active != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return active;
}
//LurCatch内存缓存
EngineResource> cached = loadFromCache(key);
if (cached != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return cached;
}
return null;
}
从上面的loadFromMemory方法中,可以发现首先读取的是弱引用内存缓存部分的内容,如果弱引用缓存中没有,然后读取的是LurCatch内存缓存,缓存的读取是根据key进行读取,key是一个接口重写了equals、hashCode、toString方法,key值和图片宽高,资源转化的class,以及其他配置信息相关,也就是只要图片中这些值的任意一个信息改变了,那么key就变了,缓存信息也就变了,虽然原始图片一样,但是展示的大图,小图,圆图是尺寸不一样,缓存的也不是一张图片了。在缓存key中内存缓存的是EngineKey,磁盘缓存的是DataCacheKey。
Glide的内存缓存分为弱引用内存缓存和LruCatch内存缓存。
1、弱引用内存缓存
@Nullable
private EngineResource> loadFromActiveResources(Key key) {
EngineResource> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
ActiveResources为glide的Map内存缓存管理类,提供了弱引用内存缓存的缓存,获取,清除等。如果读的内存缓存不为空,采用引用记数法acquired加1,当释放内存资源的时候acquired又减1。acquired的作用是的保证资源内存释放或者资源回收的正确性。下面通过get方法来看他是怎么获取的:
@Nullable
synchronized EngineResource> get(Key key) {
//第一步,通过key拿去Map集合弱引用数据
ResourceWeakReference activeRef = activeEngineResources.get(key);
if (activeRef == null) {
return null;
}
//第二步,通过弱引用拿去资源数据
EngineResource> active = activeRef.get();
if (active == null) {
cleanupActiveReference(activeRef);
}
return active;
}
可以看出弱引用内存内存缓存是通过两步走的:
第一步:通过key拿去Map集合弱引用数据,key为缓存的键,value为一个资源的弱引用,如果Map中没有,则返回空。
第二步:通过弱引用拿去资源数据,弱引用中没有,则从Map中移除该key,再返回该对象。
2、LruCatch内存缓存
private EngineResource> loadFromCache(Key key) {
EngineResource> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
Glide使用的LruCatch缓存不是原生的LruCatch,而是自定义的LruCatch,实现类为LruResourceCache,他管理了LruCatch内存缓存。如果LruCatch内存缓存资源存在则处理和上面的Map内存缓存一样,引用记数法acquired加1,在回收或者是放内存时候-1,同时将该资源放到内存缓存的Map集合中,以便于下次再从Map内存缓存中读取。下面看看具体如何读取LruCatch内存缓存:
private EngineResource> getEngineResourceFromCache(Key key) {、
//移除之前的LruCatch缓存内存资源(为啥要移除没看明白)
Resource> cached = cache.remove(key);
final EngineResource> result;
if (cached == null) {
//LruCatch缓存为空,则表示没有缓存,走网络
result = null;
} else if (cached instanceof EngineResource) {
//如果为EngineResource,则表示有内存缓存
// Save an object allocation if we've cached an EngineResource (the typical case).
result = (EngineResource>) cached;
} else {
//其他情况,这重新创建EngineResource(自定义的EngineResource)
result =
new EngineResource<>(
cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
}
可以看出LruCatch内存缓存是通过两步走的,流程图如下:
第一步:获取到资源,移除之前的LruCatch缓存的资源。
第二步:如果资源不为空,则返回。
磁盘缓存的默认目录:
String DEFAULT_DISK_CACHE_DIR = "image_manager_disk_cache";
默认大小:
int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;
配置选项:
当内存的两级缓存没拿到数据时候,会执行waitForExistingOrStartNewJob方法:
private LoadStatus waitForExistingOrStartNewJob(*******省略参数********) {
//从EngineJob缓存Map集合中获取job任务
//onlyRetrieveFromCache = true表示只从磁盘中获取,=false表示可以从网络中获取对应两个Map集合,根据key获取value
EngineJob> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
//开启回调线程池,设置回调
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
}
//EngineJob:负责请求回调
EngineJob engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
//DecodeJob:负责从缓存数据或原始源解码资源的类以及应用转换和转码
DecodeJob decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
//将请求任务放到Map里面
jobs.put(key, engineJob);
//开启缓存线程池
engineJob.addCallback(cb, callbackExecutor);
//开启网络请求线程池
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
磁盘缓存有两个关键类EngineJob和DecodeJob,其中EngineJob负责回调部分,DecodeJob负责磁盘读取和网络请求的编码和解码部分。二者的创建是采用工厂设计模式从缓存池里面读取的,缓存池pool的最大容量为150,数据结构为数组。步骤如下:
1、从Map
2、如果EngineJob缓存为空,创建DecodeJob和EngineJob,将EngineJob添加到缓存集合中,一并交给DecodeJob,开启资源加载线程池,进行磁盘缓存加载或者网络请求。流程如下:
engineJob.start(decodeJob) -> DecodeJob.run() -> runWrapped() -> startNext()
首次进来runReason默认为INITIALIZE,调用runWrapped如下:
private void runWrapped() {
switch (runReason) {
//首次进来执行,默认数据执行完毕之后,status = RESOURCE_CACHE; currentGenerator = ResourceCacheGenerator
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);
}
}
currentGenerator.startNext()这里不在详细深入了,主要是加载数据,对应的接口是DataFetcherGenerator三个实现类分别是:
runGenerators是一个while循环,默认首次进执行ResourceCacheGenerator的startNext方法,如果获取失败,通过getNextStage改变status的值为DATA_CACHE,同时currentGenerator = DataCacheGenerator,在执行DataCacheGenerator的startNext,如果再返回失败status的值为SOURCE,currentGenerator = SourceGenerator执行到这里满足条件退出循环。
private void runGenerators() {
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
}
@Override
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
退出循环之前这时候执行reschedule方法,设置任务的RunReason是SWITCH_TO_SOURCE_SERVICE,触发回调Callback的reschedule方法,EngindJob实现此回调接口, 再次调用向线程池派发该DecodeJob任务:
@Override
public void reschedule(DecodeJob> job) {
getActiveSourceExecutor().execute(job);
}
即再次执行runWrapped方法,这时候currentGenerator = SourceGenerator,runReason = SWITCH_TO_SOURCE_SERVICE,会在此执行SourceGenerator的startNext方法,如下:
@Override
public boolean startNext() {
//第二次执行时候进行缓存
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
//缓存成功后,return不执行网络请求了。
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;
}
第一次网络请求时候,在loadData中具体的请求是由XxxFetcher(HttpUrlFetcher等)中的loadData方法进行请求,在4.10.0版本时使用的HttpURLConnection进行获取图片资源Stream的,当获取成功时候会调用callback.onDataReady(result);callback为SourceGenerator中的监听接口,将结果返回给SourceGenerator方法中的onDataReady,同时数据保存在SourceGenerator内部 dataToCache变量中,接着第二次触发reschedule()方法:
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
//数据保存在SourceGenerator内部
dataToCache = data;
//再次触发DecodeJob中的reschedule
cb.reschedule();
} else {
cb.onDataFetcherReady(
loadData.sourceKey,
data,
loadData.fetcher,
loadData.fetcher.getDataSource(),
originalKey);
}
}
次执行runWrapped方法,和上面的一样,会在此执行SourceGenerator的startNext方法,这时候dataToCache已经有数值了,这时候才会调用cacheData(data);方法进行缓存,这块才是Glide设置缓存的地方。
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {
//创建缓存内容
Encoder
最后会创建DataCacheGenerator对象,cacheData方法执行完毕,接着执行SourceGenerator的startNext后面的内容:
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator.startNext()这个判断会进入sourceCacheGenerator的startNext()方法,这时候将从Disk缓存中获取一次,并且设置LruCatch缓存,成功返回。DataCacheGenerator的startNext方法。最终也会走到sourceCacheGenerator的loadData.fetcher.loadData(helper.getPriority(), this);这里的loadData方法是ByteBufferFetcher的loadData,ByteBufferFetcher是一个专门读取缓存的类,通过ByteBufferUtil.fromFile(file)拿到缓存,调用callback.onDataReady(result)设置回调,callback为DataCacheGenerator。
@Override
public void loadData(
@NonNull Priority priority, @NonNull DataCallback super ByteBuffer> callback) {
ByteBuffer result;
try {
//读取缓存
result = ByteBufferUtil.fromFile(file);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to obtain ByteBuffer for file", e);
}
callback.onLoadFailed(e);
return;
}
//设置回调,callback为DataCacheGenerator
callback.onDataReady(result);
}
@Override
public void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
}
最后将结果回传给DecodeJob的onDataFetcherReady,然后调用decodeFromRetrievedData();方法将结果回传。
@Override
public void onDataFetcherReady(
Key sourceKey, Object data, DataFetcher> fetcher, DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;
this.currentData = data;
this.currentFetcher = fetcher;
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
//当前线程是否和runGenerators方法时的线程相等
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
DecodeJob -> onDataFetcherReady -> decodeFromRetrievedData() -> notifyEncodeAndRelease()-> notifyComplete(result, dataSource) -> callback.onResourceReady(resource, dataSource)-> engineJob.onResourceReady(resource, dataSource) -> notifyCallbacksOfResult()
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource> localResource;
synchronized (this) {
stateVerifier.throwIfRecycled();
if (isCancelled) {
//取消请求则回收资源
resource.recycle();
release();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
} else if (hasResource) {
throw new IllegalStateException("Already have resource");
}
//创建engineResource
engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener);
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
//设置弱引用内存缓存
engineJobListener.onEngineJobComplete(this, localKey, localResource);
for (final ResourceCallbackAndExecutor entry : copy) {
//执行资源回调线程,执行run方法返回回调
entry.executor.execute(new CallResourceReady(entry.cb));
}
decrementPendingCallbacks();
}
notifyCallbacksOfResult收到回调的数据,会执行三步:
1、如果发现取消了请求则立即释放资源
2、回调Engine,设置弱引用内存缓存
@Override
public synchronized void onEngineJobComplete(
EngineJob> engineJob, Key key, EngineResource> resource) {
if (resource != null && resource.isMemoryCacheable()) {
//弱引用缓存
activeResources.activate(key, resource);
}
jobs.removeIfCurrent(key, engineJob);
}
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();
}
}
3、执行资源回调线程,执行run方法返回回调。
EngineJob : CallResourceReady.run -> callCallbackOnResourceReady(cb) -> cb.onResourceReady(engineResource, dataSource) ->
SingleRequest : onResourceReady -> onResourceReady ->
target: onResourceReady(result, animation)
这样又回到起点所说的target上面去了,调用onResourceReady设置图片资源。至此磁盘缓存结束。简化流程图如下:
在内存缓存中Bitmap是内存消耗的主要元凶,Glide是通过Bitmap的缓存池进行缓存, BitmapPool缓存Bitmap 对象,避免重复创建Bitmap避免资源的过渡消耗。
1、BitmapPool
public interface BitmapPool {
long getMaxSize();
void setSizeMultiplier(float var1);
void put(Bitmap var1);
@NonNull
Bitmap get(int var1, int var2, Config var3);
@NonNull
Bitmap getDirty(int var1, int var2, Config var3);
void clearMemory();
void trimMemory(int var1);
}
他是一个接口,定义了缓存的读取、清理,设置,BitmapPool的实现类,在 Glide 中有两个 BitmapPool 的实现类:BitmapPoolAdapter 和 LruBitmapPool。 BitmapPoolAdapter 类是一个空实现类,这里详细分析一下 LruBitmapPool,在分析之前先来了解下LruPoolStrategy、Key 和 KeyPool。
(1)LruPoolStrategy
LruPoolStrategy定义了LruBitmapPool缓存策略接口,包括一些存放、获取、移除Bitmap的方法。在 Glide内部 LruPoolStrategy接口有三个实现类,分别是 AttributeStrategy、SizeConfigStrategy 和 SizeStrategy 类,这三个类是通过不同维度的条件缓存 Bitmap。
(2)Key和KeyPool
BitmapPool 和 LruPoolStrategy 都只是接口定义,其底层的实现类其实都是使用了Map 数据结构,Map 是以
2、缓存原理
LruBitmapPool是BitmapPool的实现类,可以通过Glide的配置项配置builder.setBitmapPool。LruBitmapPool没有做太多的东西主要任务都交给了 LruPoolStrategy缓存策略接口,具体的实现类有 AttributeStrategy、SizeConfigStrategy 和 SizeStrategy,这三个类是通过不同的条件来缓存 Bitmap 的,底层具体的实现都使用了GroupedLinkedMap,这是Glide为了实现LRU算法自定义的一个数据结构,其中包含三种数据结构:哈希表(HashMap)、循环链表以及列表(ArrayList)。这个结构其实类似 Java 里提供的 LinkedHashMap 类。如下图:
class GroupedLinkedMap {
private final GroupedLinkedMap.LinkedEntry head = new GroupedLinkedMap.LinkedEntry();
private final Map> keyToEntry = new HashMap();
GroupedLinkedMap() {
}
//设置
public void put(K key, V value) {
GroupedLinkedMap.LinkedEntry entry = (GroupedLinkedMap.LinkedEntry)this.keyToEntry.get(key);
if (entry == null) {
entry = new GroupedLinkedMap.LinkedEntry(key);
this.makeTail(entry);
this.keyToEntry.put(key, entry);
} else {
key.offer();
}
entry.add(value);
}
//读取
@Nullable
public V get(K key) {
GroupedLinkedMap.LinkedEntry entry = (GroupedLinkedMap.LinkedEntry)this.keyToEntry.get(key);
if (entry == null) {
entry = new GroupedLinkedMap.LinkedEntry(key);
this.keyToEntry.put(key, entry);
} else {
key.offer();
}
this.makeHead(entry);
return entry.removeLast();
}
}
BitmapPool 大小通过 MemorySizeCalculator 设置,使用LRU算法维护BitmapPool,会根据 Bitmap 的大小与 Config 生成一个 Key,Key 也有自己对应的对象池,数据最终存储在GroupedLinkedMap 中,GroupedLinkedMap使用哈希表、循环链表、List 来存储数据。
1、先读取内存缓存,内存缓存分为两步第一步根据key读取Map缓存的弱引用缓存,第二步弱引用读取LruCatch缓存。内促资源存在则返回,否则执行磁盘缓存。
2、再去取磁盘缓存,如果没有,则请求网络,结束后依次设置磁盘缓存,内存LruCatch缓存,内存弱引用缓存,再回调给ImageView。