glide缓存分为内存缓存和磁盘缓存。内存缓存分为活动缓存和cache。磁盘缓存又分为resource和data。本文将围绕加载图片流程介绍glide的缓存。
Glide.with(this)
.load(url)
.skipMemoryCache(true)
// .transform(CircleCrop())
// .error(R.drawable.ic_launcher_background)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.listener(getListener(index))
.into(mIv)
1、load
public <R> LoadStatus load(
GlideContext glideContext,
Object model) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height);
EngineResource<?> memoryResource;
synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
return waitForExistingOrStartNewJob(
glideContext,
model,
signature,
width,
height);
}
}
cb.onResourceReady(
memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return null;
}
1.1、loadFromMemory 先从内从中加载。
//Engine
private EngineResource<?> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
return active;
}
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
return cached;
}
return null;
}
可以看到从内存加载又分为活动缓存和 cache
1.11、活动缓存
举例: 一个LinearLayout中有10个imageview,通过glide加载。那么这10个图片就缓存在activityResource中。 当点击的条目,删除一个item. 这个ActivityResource缓存并没有被移除。当gc运行时,将这个item回收掉之后了,就会将这个缓存加入到resourceReferenceQueue,然后通过遍历这个引用队列,从活动缓存中移除。
举例: 当在recyclerview的每个item加载一个imagview。 那么这个活动缓存的最大数目为= 可见数据+ 预加载数目 + 复用池1 ,因为从recyclerview移除之后,不一定就被立即回收了,当从复用池中复用item时,会主动释放上一次item对应的活动缓存。此时活动缓存就减少了。
活动缓存是何时存入的了?
1)、从缓存中获取到资源时,加入到活动缓存。
private EngineResource<?> loadFromCache(Key key) {
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
2)、从网络和磁盘加载资源完成时,加入到活动缓存。
活动缓存何时删除的了?
1)、onDestroy时,request调用clear() 会释放当前对应key的活动缓存。
2)、不能设置资源给target,释放当前key的活动缓存。
3)、获取活动缓存,发现该活动缓存EngineResource不存在,就删除key的活动缓存。也就是setIsActiveResourceRetentionAllowed(false)的情况。
4)、创建ActiveResources对象时,就开启了一个线程,一直轮询这个queue,如果存在的EngineResource,就删除对应的活动缓存。
1.12、内存缓存
获取内存时就从内存缓存中移除。
//Engine
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);
final EngineResource<?> result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
// Save an object allocation if we've cached an EngineResource (the typical case).
result = (EngineResource<?>) cached;
} else {
result =
new EngineResource<>(
cached,
/* isMemoryCacheable= */ true,
/* isRecyclable= */ true,
key,
/* listener= */ this);
}
return result;
}
何时加入内存
从活动缓存移除就加入了内存缓存。内存缓存超过容量后被释放的bitmap等会被加入到bitmapPool中。从活动缓存中移除的要么加入内存缓存,要么加入到bitmapPool中。
//Engine
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isMemoryCacheable()) {
//加入内存缓存
cache.put(cacheKey, resource);
} else {
//资源释放,bitmap支持复用则加入到bitmapPool
resourceRecycler.recycle(resource, /* forceNextFrame= */ false);
}
}
//BitmapResource
@Override
public void recycle() {
bitmapPool.put(bitmap);
}
缓存资源的key是通过宽高,加载图片的model、options等生成的。所以宽高变化,可能导致内存中找不到这张图。
1.2、内存不存在,开启一个job从网络加载加载。
private <R> Engine.LoadStatus waitForExistingOrStartNewJob(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height) {
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
return new Engine.LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
return new Engine.LoadStatus(cb, engineJob);
}
可以看到先判断,是否是从从缓存中取获取缓存job。
如果缓存的job没有找到。则创建一个job。并且执行这个RunableJob。
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
所以接下来,我们看下这个runnable(DecodeJob)的run方法。
public void run() {
DataFetcher<?> localFetcher = currentFetcher;
try {
runWrapped();
}
}
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;
}
}
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
}
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:
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
}
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);
}
}
可以看到,根据当前的状态和diskCacheStrategy磁盘策略的配置,来决定是加载RESOURCE_CACHE、DATA_CACHE、SOURCE。
RESOURCE_CACHE : 是转换过或者改变过采样率的文件。解码之后的。
DATA_CACHE:原始的未修改的数据。解码之前的。
SOURCE :网络文件,源文件。
1.3、INITIALIZE
如果配置全部允许,会首先去加载Resource_cache,再加载DATA_CACHE,如果都加载不了,再从网络加载SOURCE。
ResourceCacheGenerator和DataCacheGenerator会从磁盘缓存中加载数据,这里不看了,看下从Remote加载,SourceGenerator从网络加载数据。
// SourceGenerator
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
boolean isDataInCache = cacheData(data);
dataToCache = null;
}
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;
startNextLoad(loadData);
}
}
return started;
}
private void startNextLoad(final ModelLoader.LoadData<?> toStart) {
loadData.fetcher.loadData(
helper.getPriority(),
new DataFetcher.DataCallback<Object>() {
@Override
public void onDataReady(@Nullable Object data) {
if (isCurrentRequest(toStart)) {
onDataReadyInternal(toStart, data);
}
}
@Override
public void onLoadFailed(@NonNull Exception e) {
if (isCurrentRequest(toStart)) {
onLoadFailedInternal(toStart, e);
}
}
});
}
void onDataReadyInternal(LoadData<?> loadData, 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);
}
}
首先从网络loadData.fetcher.loadData中加载,然后判断isDataCacheable()是否可以缓存data,这个配置是缓存策略中配置的。如果可以缓存,调用cb.reschedule()。
1.4、SWITCH_TO_SOURCE_SERVICE
private void reschedule(RunReason runReason) {
this.runReason = runReason;
callback.reschedule(this);
}
@Override
public void reschedule() {
reschedule(RunReason.SWITCH_TO_SOURCE_SERVICE);
}
这个方法会再次调用run()方法,状态变为SWITCH_TO_SOURCE_SERVICE从网络获取数据切换到从磁盘获取, 还会再次执行要这个类。 此时startNext方法中的dataToCache不为空,并且会调用将数据缓存到磁盘中,然后调用DataCacheGenerator.startNext(),调用磁盘缓存加载,返回磁盘加载的结果。
// SourceGenerator
private boolean cacheData(Object dataToCache) throws IOException {
try {
DataRewinder<Object> rewinder = helper.getRewinder(dataToCache);
Object data = rewinder.rewindAndGet();
Encoder<Object> encoder = helper.getSourceEncoder(data);
DataCacheWriter<Object> writer = new DataCacheWriter<>(encoder, data, helper.getOptions());
DataCacheKey newOriginalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
DiskCache diskCache = helper.getDiskCache();
diskCache.put(newOriginalKey, writer);
if (diskCache.get(newOriginalKey) != null) {
originalKey = newOriginalKey;
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
return true;
} else {
cb.onDataFetcherReady(
loadData.sourceKey,
rewinder.rewindAndGet(),
loadData.fetcher,
loadData.fetcher.getDataSource(),
loadData.sourceKey);
}
return false;
}
}
上面就是在SourceGenerator中缓存数据Stream到磁盘过程。
1.5、DECODE_DATA
从getNextStage分析完INITIALIZE、SWITCH_TO_SOURCE_SERVICE,接下看下DECODE_DATA状态,从网络请求对应的HttpGlideUrlLoader,返回的输入流数据是ContentLengthInputStream。
从磁盘缓存对应的FileLoader,返回的输入流是文件输入流。
对于这些流需要转化成Bitmap、Drawable等。
decode的过程 就是将InputStream 转化成bitmap的过程。
//DecodeJob
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource, isLoadingFromAlternateCacheKey);
} else {
runGenerators();
}
}
//DecodePath
public Resource<Transcode> decode(
DataRewinder<DataType> rewinder,
int width,
int height,
@NonNull Options options,
DecodeCallback<ResourceType> callback)
throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
//DecodeJob
<Z> Resource<Z> onResourceDecoded(DataSource dataSource, @NonNull Resource<Z> decoded) {
@SuppressWarnings("unchecked")
Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
Transformation<Z> appliedTransformation = null;
Resource<Z> transformed = decoded;
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
transformed = appliedTransformation.transform(glideContext, decoded, width, height);
}
final EncodeStrategy encodeStrategy;
final ResourceEncoder<Z> encoder;
if (decodeHelper.isResourceEncoderAvailable(transformed)) {
encoder = decodeHelper.getResultEncoder(transformed);
encodeStrategy = encoder.getEncodeStrategy(options);
} else {
encoder = null;
encodeStrategy = EncodeStrategy.NONE;
}
Resource<Z> result = transformed;
boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
if (diskCacheStrategy.isResourceCacheable(
isFromAlternateCacheKey, dataSource, encodeStrategy)) {
final Key key;
switch (encodeStrategy) {
case SOURCE:
key = new DataCacheKey(currentSourceKey, signature);
break;
case TRANSFORMED:
key =
new ResourceCacheKey(
decodeHelper.getArrayPool(),
currentSourceKey,
signature,
width,
height,
appliedTransformation,
resourceSubClass,
options);
break;
default:
throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
}
LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
deferredEncodeManager.init(key, encoder, lockedResult);
result = lockedResult;
}
return result;
}
上面第一步是decode的流程,将stream转化成bitmap,设置采样率等,transformed,可以看到在解析完成调用了diskCacheStrategy.isResourceCacheable()这个方法主要是为了初始化Resouce_cache相关信息。
// DecodeJob
private void notifyEncodeAndRelease(
Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
GlideTrace.beginSection("DecodeJob.notifyEncodeAndRelease");
try {
Resource<R> result = resource;
LockedResource<R> lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
notifyComplete(result, dataSource, isLoadedFromAlternateCacheKey)
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
onEncodeComplete();
} finally {
GlideTrace.endSection();
}
}
private void notifyComplete(
Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource, isLoadedFromAlternateCacheKey);
}
void encode(DiskCacheProvider diskCacheProvider, Options options) {
GlideTrace.beginSection("DecodeJob.encode");
try {
diskCacheProvider
.getDiskCache()
.put(key, new DataCacheWriter<>(encoder, toEncode, options));
} finally {
toEncode.unlock();
GlideTrace.endSection();
}
}
notifyComplete就是调用callback.onResourceReady这个方法最终就回调到我们给request设置的RequestListener中。
上面我们知道缓存Resource相关key已经初始化了,接着deferredEncodeManager.encode将resource缓存在本地,整个加载过程就结束。
上面我们一直提到磁盘缓存策略,那么我们看一下DiskCacheStrategy.ALL的代码.
public static final DiskCacheStrategy ALL =
new DiskCacheStrategy() {
@Override
public boolean isDataCacheable(DataSource dataSource) {
return dataSource == DataSource.REMOTE;
}
@Override
public boolean isResourceCacheable(
boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
return dataSource != DataSource.RESOURCE_DISK_CACHE
&& dataSource != DataSource.MEMORY_CACHE;
}
@Override
public boolean decodeCachedResource() {
return true;
}
@Override
public boolean decodeCachedData() {
return true;
}
};
isDataCacheable 表示可以将原始数据data缓存。
isResourceCacheable 表示可以将Resouce数据缓存。
decodeCachedResource 表示可以从Resource 中加载数据。
decodeCachedData 表示可以从Data中加载数据。
上面这幅图展示了glide缓存加载流程,没有BitmapPool,BitmapPool 只是从磁盘缓存、网络加载图片时,decode图片需要复用的之前图片的内存,从bitmappool中获取。 活动内存释放、cache的大小超过了容量会加入到bitmapPool。