Glide 缓存机制
Posted qxs`
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Glide 缓存机制相关的知识,希望对你有一定的参考价值。
Glide 缓存机制
@Nullable 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; //退出app,在次进入,弱引用已被清理,从LurCached缓存加载 EngineResource<?> cached = loadFromCache(key); if (cached != null) if (VERBOSE_IS_LOGGABLE) logWithTimeAndKey("Loaded resource from cache", startTime, key); return cached; return null;
Glide 在加载图片前会查询弱引用中是否有图片,如果有会直接加载,如果没有就会去找
LruCached.
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);
// Avoid calling back while holding the engine lock, doing so makes it easier for callers to
// deadlock.
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
如果LruCached 也不存在,会查找磁盘缓存,如果磁盘缓存也没有才会进行网络请求,Glide 网络请求类HttpUrlFetcher
private <R> LoadStatus waitForExistingOrStartNewJob(
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,
EngineKey key,
long startTime)
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null)
current.addCallback(cb, callbackExecutor);
if (VERBOSE_IS_LOGGABLE)
logWithTimeAndKey("Added to existing load", startTime, key);
return new LoadStatus(cb, current);
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);
if (VERBOSE_IS_LOGGABLE)
logWithTimeAndKey("Started new load", startTime, key);
return new LoadStatus(cb, engineJob);
EngineJob 是一个线程对象,调用 strat 方法开启一个线程池,执行EngineJob线程的run 方法
class DecodeJob<R>
implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable
public synchronized void start(DecodeJob<R> decodeJob)
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
//线程池
executor.execute(decodeJob);
在run 方法中runWapped()方法 -->DecodeJob
@Override
public void run()
......
try
if (isCancelled)
notifyFailed();
return;
runWrapped();
catch (CallbackException e)
catch (Throwable t)
if (stage != Stage.ENCODE)
throwables.add(t);
notifyFailed();
finally
if (localFetcher != null)
localFetcher.cleanup();
GlideTrace.endSection();
在runWraoped()方法中分为三个swith 分别 INITIALIZE SWITCH_TO_SOURCE_SERVICE DECODE_DATA 默认走第一个INITIALIZE
/**
* 读磁盘缓存
*/
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);
在第一个 INITIALIZE 方法中调用 runGenerators方法 -->DecodeJob
private void runGenerators()
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
//currentGenerator.startNext()
&& !(isStarted = currentGenerator.startNext()))
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE)
reschedule();
return;
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted)
notifyFailed();
// Otherwise a generator started a new load and we expect to be called back in
// onDataFetcherReady.
在runGenerators 方法中调用currentGenerator.startNext() 这个currentGenerator 调的是 ResourceCacheGenerator startNext 方法
public boolean startNext()
//添加到LruCache中
List<Key> sourceIds = helper.getCacheKeys();
if (sourceIds.isEmpty())
return false;
List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
if (resourceClasses.isEmpty())
if (File.class.equals(helper.getTranscodeClass()))
return false;
throw new IllegalStateException(
"Failed to find any load path from "
+ helper.getModelClass()
+ " to "
+ helper.getTranscodeClass());
while (modelLoaders == null || !hasNextModelLoader())
resourceClassIndex++;
if (resourceClassIndex >= resourceClasses.size())
sourceIdIndex++;
if (sourceIdIndex >= sourceIds.size())
return false;
resourceClassIndex = 0;
Key sourceId = sourceIds.get(sourceIdIndex);
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = helper.getTransformation(resourceClass);
// PMD.AvoidInstantiatingObjectsInLoops Each iteration is comparatively expensive anyway,
// we only run until the first one succeeds, the loop runs for only a limited
// number of iterations on the order of 10-20 in the worst case.
//key
currentKey =
new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoops
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;
//添加到LruCache中 然后在创建存储key 通过 key 找到磁盘存储的文件 然后调用 loadData.fetcher.loadData(helper.getPriority(), this); -->ByteBufferFileLoader
@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.onDataReady(result);
然后调用onDataReady -->ResourceCacheGenerator
@Override
public void onDataReady(Object data)
cb.onDataFetcherReady(
sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE, currentKey);
然后调用 onDataFetcherReady -->DecodeJob
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;
if (Thread.currentThread() != currentThread)
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
else
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try
//转码
decodeFromRetrievedData();
finally
GlideTrace.endSection();
然后调用 decodeFromRetrievedData(); -->DecodeJob
private void decodeFromRetrievedData()
if (Log.isLoggable(TAG, Log.VERBOSE))
logWithTimeAndKey(
"Retrieved data",
startFetchTime,
"data: "
+ currentData
+ ", cache key: "
+ currentSourceKey
+ ", fetcher: "
+ currentFetcher);
Resource<R> resource = null;
try
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
catch (GlideException e)
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
if (resource != null)
notifyEncodeAndRelease(resource, currentDataSource);
else
runGenerators();
然后调用 decodeFromData 解码到 Resource Resource 不等于空调用 notifyEncodeAndRelease -->DecodeJob
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource)
if (resource instanceof Initializable)
((Initializable) resource).initialize();
Resource<R> result = resource;
LockedResource<R> lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode())
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
//写入弱引用
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try
//写入磁盘
if (deferredEncodeManager.hasResourceToEncode())
deferredEncodeManager.encode(diskCacheProvider, options);
finally
if (lockedResource != null)
lockedResource.unlock();
// Call onEncodeComplete outside the finally block so that it's not called if the encode process
// throws.
onEncodeComplete();
然后调用 notifyComplete -->DecodeJob
private void notifyComplete(Resource<R> resource, DataSource dataSource)
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
然后调用 onResourceReady --> EngineJob
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource)
synchronized (this)
this.resource = resource;
this.dataSource = dataSource;
notifyCallbacksOfResult();
然后调用 notifyCallbacksOfResult- -> EngineJob
@Synthetic
void notifyCallbacksOfResult()
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this)
stateVerifier.throwIfRecycled();
if (isCancelled)
// TODO: Seems like we might as well put this in the memory cache instead of just recycling
// it since we've gotten this far...
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 = engineResourceFactory.build(resource, isCacheable, key, resourceListener);
// Hold on to resource for duration of our callbacks below so we don't recycle it in the
// middle of notifying if it synchronously released by one of the callbacks. Acquire it under
// a lock here so that any newly added callback that executes before the next locked section
// below can't recycle the resource before we call the callbacks.
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
//保存弱引用
engineJobListener.onEngineJobComplete(this, localKey, localResource);
for (final ResourceCallbackAndExecutor entry : copy)
//CallResourceReady 线程run方法
entry.executor.execute(new CallResourceReady(entry.cb));
decrementPendingCallbacks();
@SuppressWarnings("WeakerAccess")
@Synthetic
synchronized void incrementPendingCallbacks(int count)
Preconditions.checkArgument(isDone(), "Not yet complete!");
if (pendingCallbacks.getAndAdd(count) == 0 && engineResource != null)
engineResource.acquire();
executor 线程池调用 CallResourceReady 线程run方法 -->CallResourceReady
private class CallResourceReady implements Runnable
private final ResourceCallback cb;
CallResourceReady(ResourceCallback cb)
this.cb = cb;
@Override
public void run()
// Make sure we always acquire the request lock, then the EngineJob lock to avoid deadlock
// (b/136032534).
synchronized (cb.getLock())
synchronized (EngineJob.this)
if (cbs.contains(cb))
// Acquire for this particular callback.
engineResource.acquire();
callCallbackOnResourceReady(cb);
removeCallback(cb);
decrementPendingCallbacks();
然后调用 callCallbackOnResourceReady - -> EngineJob
@SuppressWarnings("WeakerAccess")
@Synthetic
@GuardedBy("this")
void callCallbackOnResourceReady(ResourceCallback cb)
try
// This is overly broad, some Glide code is actually called here, but it's much
// simpler to encapsulate here than to do so at the actual call point in the
// Request implementation.
cb.onResourceReady(engineResource, dataSource);
catch (Throwable t)
throw new CallbackException(t);
然后调用 onResourceReady -->SingleRequest
@Override
public void onResourceReady(Resource<?> resource, DataSource dataSource)
stateVerifier.throwIfRecycled();
Resource<?> toRelease = null;
try
synchronized (requestLock)
loadStatus = null;
if (resource == null)
onLoadFailed(exception);
return;
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass()))
toRelease = resource;
this.resource = null;
onLoadFailed(exception);
return;
if (!canSetResource())
toRelease = resource;
this.resource = null;
// We can't put the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
onResourceReady((Resource<R>) resource, (R) received, dataSource);
finally
if (toRelease != null)
engine.release(toRelease);
然后调用 onResourceReady -->SingleRequest
@GuardedBy("requestLock")
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource)
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
if (glideContext.getLogLevel() <= Log.DEBUG)
isCallingCallbacks = true;
try
boolean anyListenerHandledUpdatingTarget = false;
if (requestListeners != null)
for (RequestListener<R> listener : requestListeners)
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget)
Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
finally
isCallingCallbacks = false;
notifyLoadSuccess();
target 为 ImageViewTarget
target.onResourceReady(result, animation); --> ImageViewTarget
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition)
if (transition == null || !transition.transition(resource, this))
setResourceInternal(resource);
else
maybeUpdateAnimatable(resource);
然后调用 setResourceInternal --> ImageViewTarget
private void setResourceInternal(@Nullable Z resource)
// Order matters here. Set the resource first to make sure that the Drawable has a valid and
// non-null Callback before starting it.
setResource(resource);
maybeUpdateAnimatable(resource);
@Override
protected void setResource(@Nullable Drawable resource)
Log.d(TAG, "setResource: -----"+resource);
view.setImageDrawable(resource);
这就是图片加载的整体流程,首先Glide会查询弱引用,如果弱引用没有,会查找LruCached,如果LruCached也没有会查找DiskLruCache 磁盘缓存,如果磁盘缓存也没有,才会进行网络请求,请求到数据会同步到弱引用,LruCached,DiskLruCache,这就是整体策略,这还要注意,如果磁盘缓存有图片,就不会网络请求,会同步到弱引用和LruCache 中.
以上是关于Glide 缓存机制的主要内容,如果未能解决你的问题,请参考以下文章