Glide 4.11.0 Send request preparation, this article analyzes the request and cache of Glide. Requests end up wrapped as SingleRequest.
directory
Get pictures from memory 3. Get pictures from disk 4. Get Pictures from network 5
One request entry
request.begin(); The begin() method of SingleRequest is executed
SingleRequest.java
@Override
public void begin(a) {
/ / synchronization locks
synchronized(requestLock) {...if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
// Where the actual request and cache are
onSizeReady(overrideWidth, overrideHeight);
} else{···} ···}}Copy the code
Use engine.load () to load image data
SingleRequest.java
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if(status ! = Status.WAITING_FOR_SIZE) {return;
}
status = Status.RUNNING;
/ / thumbnails
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier); ... loadStatus = engine. The 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); ...}}Copy the code
First the static factory method creates the engine key, using signature, width, height, transform, resource class, configuration, and so on as the parameters to generate the key
Engine.java
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
intheight, 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;
// Create a key to load the engine through a static factory
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
// Fetch data from memoryEngineResource<? > memoryResource;synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
//
returnwaitForExistingOrStartNewJob( 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;
Copy the code
Fetch the image from memory
2.1 Retrieving images from memory
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime); Load images from memory
Engine.java
privateEngineResource<? > loadFromMemory( EngineKey key,boolean isMemoryCacheable, long startTime) {
// If no image is fetched from memory, null is returned
if(! isMemoryCacheable) {return null;
}
// Get the resource being displayedEngineResource<? > active = loadFromActiveResources(key);if(active ! =null) {...return active;
}
// Load images from the cacheEngineResource<? > cached = loadFromCache(key);if(cached ! =null) {...return cached;
}
return null;
}
Copy the code
2.1.1 Loading Image Data from Active Images & Increasing image reference count
loadFromActiveResources(key);
Engine.java
privateEngineResource<? > loadFromActiveResources(Key key) {// Fetch the image from the cacheEngineResource<? > active = activeResources.get(key);if(active ! =null) {
// Increase the reference count
active.acquire();
}
return active;
}
Copy the code
2.1.1.1 Load image data from the active cache
ActiveResources.java
synchronizedEngineResource<? > get(Key key) {// Weak references decorate image references
ResourceWeakReference activeRef = activeEngineResources.get(key);
if (activeRef == null) {
return null; } EngineResource<? > active = activeRef.get();if (active == null) {
// Clear if active is null
cleanupActiveReference(activeRef);
}
// Return cached data
return active;
}
Copy the code
2.2.1 Loading images from the cache
loadFromCache(key); By loading images in the cache
Engine.java
privateEngineResource<? > loadFromCache(Key key) {// Retrieve the image from the cacheEngineResource<? > cached = getEngineResourceFromCache(key);if(cached ! =null) {
// Increase the reference count
cached.acquire();
// Add to the active cache
activeResources.activate(key, cached);
}
return cached;
}
Copy the code
getEngineResourceFromCache(key); Get image data from the cache
Engine.java
privateEngineResource<? > getEngineResourceFromCache(Key key) { Resource<? > cached = cache.remove(key);finalEngineResource<? > result;// Encapsulate the return 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;
}
Copy the code
2.2.2 Initialization of cache
The cache obtained above is of the MemoryCache type and can be set externally and assigned at Builder Glide configuration time
GlideBuilder.java
@NonNull
Glide build(@NonNull Context context) {...if (memoryCache == null) {
// Memory cache
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (engine == null) {
engine =
new Engine(
// Here memoryCache is passed to the load enginememoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor, GlideExecutor.newUnlimitedSourceExecutor(), animationExecutor, isActiveResourceRetentionAllowed); }...return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
isLoggingRequestOriginsEnabled,
isImageDecoderEnabledForBitmaps);
}
Copy the code
2.2.3 Memory Cache LruResourceCache
LruResourceCache inherits from LruCache and internally uses LinkedHashMap to realize LRU algorithm. The initial size is 100 and the loading factor is 0.75. All methods are synchronized modification to ensure synchronization
Fetch pictures from disk
3.1 Create the task of obtaining and decoding pictures
Starting with Engine’s load method, if no images are fetched from memory, start creating tasks to fetch images from disk or the network
Engine.java
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
intheight, 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) {
returnwaitForExistingOrStartNewJob( glideContext, model, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, options, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache, cb, callbackExecutor, key, startTime); }}Copy the code
Engine.java
private <R> LoadStatus waitForExistingOrStartNewJob(
GlideContext glideContext,
Object model,
Key signature,
int width,
intheight, 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) {
// If there is a task, fetch it directlyEngineJob<? > current = jobs.get(key, onlyRetrieveFromCache);if(current ! =null) { current.addCallback(cb, callbackExecutor); ...return new LoadStatus(cb, current);
}
// Create a task to execute
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
// Create a decoding task
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
// Add the task to the cache
jobs.put(key, engineJob);
// Set the callback for the task
engineJob.addCallback(cb, callbackExecutor);
// Start the taskengineJob.start(decodeJob); ...return new LoadStatus(cb, engineJob);
}
Copy the code
3.2 engineJob Obtains the picture task engine
The engineJob is created using the factory method
GlideBuilder.java
Glide build(@NonNull Conext context) {...if (engine == null) {
// Create a load engine if you don't have one externally
engine =
newEngine( memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor, GlideExecutor.newUnlimitedSourceExecutor(), animationExecutor, isActiveResourceRetentionAllowed); }...}Copy the code
Engine constructor
Engine.java
public Engine(
MemoryCache memoryCache,
DiskCache.Factory diskCacheFactory,
GlideExecutor diskCacheExecutor,
GlideExecutor sourceExecutor,
GlideExecutor sourceUnlimitedExecutor,
GlideExecutor animationExecutor,
boolean isActiveResourceRetentionAllowed) {
this(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
sourceUnlimitedExecutor,
animationExecutor,
/*jobs=*/ null./*keyFactory=*/ null./*activeResources=*/ null./*engineJobFactory=*/ null./*decodeJobFactory=*/ null./*resourceRecycler=*/ null,
isActiveResourceRetentionAllowed);
}
Engine(
MemoryCache cache,
DiskCache.Factory diskCacheFactory,
GlideExecutor diskCacheExecutor,
GlideExecutor sourceExecutor,
GlideExecutor sourceUnlimitedExecutor,
GlideExecutor animationExecutor,
Jobs jobs,
EngineKeyFactory keyFactory,
ActiveResources activeResources,
EngineJobFactory engineJobFactory,
DecodeJobFactory decodeJobFactory,
ResourceRecycler resourceRecycler,
booleanIsActiveResourceRetentionAllowed) {...if (engineJobFactory == null) {
engineJobFactory =
new EngineJobFactory(
diskCacheExecutor,
sourceExecutor,
sourceUnlimitedExecutor,
animationExecutor,
/*engineJobListener=*/ this./*resourceListener=*/ this); }...}Copy the code
3.3 Start the image retrieval task
engineJob.start(decodeJob); DecodeJob run()
EngineJob.java
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
Copy the code
DecodeJob.java
public void run(a) {.../ / assignment callbackDataFetcher<? > localFetcher = currentFetcher;try {
if (isCancelled) {
notifyFailed();
return;
}
// Load method
runWrapped();
} catch (CallbackException e) {
// If a callback not controlled by Glide throws an exception, we should avoid the Glide
// specific debug logic below.
throw e;
} catch(Throwable t) {...if(stage ! = Stage.ENCODE) { throwables.add(t); notifyFailed(); }...}finally{.../ / to empty
if(localFetcher ! =null) { localFetcher.cleanup(); } GlideTrace.endSection(); }}Copy the code
Call runWrapped (); Start a mission
DecodeJob.java
private void runWrapped(a) {
switch (runReason) {
// This must be called the first time
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); }}Copy the code
getNextStage(Stage.INITIALIZE); Gets the type of loaded data based on the current state
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
// A decoded cache returns stage.resource_cache
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE
: getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
// Return stage.data_cache if there are disk meat dishes
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 stage.finished if only loaded from cache otherwise stage.source
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: "+ current); }}Copy the code
GetNextStage () returns the Stage to runWrapped() and then calls getNextGenerator() to get different ways of loading data depending on the state
DecodeJob.java
private DataFetcherGenerator getNextGenerator(a) {
switch (stage) {
case RESOURCE_CACHE:
// Fetch data from the decoded file cache
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
// Fetch data from the file cache
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
// Load data from the network
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: "+ stage); }}Copy the code
The way to get loaded data is returned to runWrapped(), called
DecodeJob.java
private void runGenerators(a) {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while(! isCancelled && currentGenerator ! =null
// This is where the load data is actually called
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return; ...}}}Copy the code
3.4 Obtaining Data from the disk cache
CurrentGenerator. StartNext () to get the data, there are two is disk cache ResourceCacheGenerator match width was obtained from the disk cache, DataCacheGenerator derives from the disk cache
3.4.1 Obtaining the Cache with the matching width and height from the disk
ResourceCacheGenerator.java
public boolean startNext(a) { List<Key> sourceIds = helper.getCacheKeys(); ... the List < Class <? >> resourceClasses = helper.getRegisteredResourceClasses(); ...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);// The type to be convertedTransformation<? > 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.
// The current cache key
currentKey =
new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
// Get the file from key
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;
// Use fetcher to load data
loadData.fetcher.loadData(helper.getPriority(), this); }}return started;
}
Copy the code
3.4.2 Obtaining cache from disk
DataCacheGenerator.java
public boolean startNext(a) {
while (modelLoaders == null| |! hasNextModelLoader()) { sourceIdIndex++;if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
Key sourceId = cacheKeys.get(sourceIdIndex);
// PMD.AvoidInstantiatingObjectsInLoops The loop iterates a limited number of times
// and the actions it performs are much more expensive than a single allocation.
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
if(cacheFile ! =null) {
this.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;
// Load data from fetcher
loadData.fetcher.loadData(helper.getPriority(), this); }}return started;
}
Copy the code
Get pictures from the Internet
4.1 Obtaining cache from the Network
CurrentGenerator. StartNext () to get the data, the realization of the network is the SourceGenerator class
SourceGenerator.java
@Override
public boolean startNext(a) {
if(dataToCache ! =null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
// Start fetching data directly if there is any in the cache
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;
// Start network request datastartNextLoad(loadData); }}return started;
}
// Start using the network to get data
private void startNextLoad(finalLoadData<? > toStart) {
loadData.fetcher.loadData(
helper.getPriority(),
new 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); }}}); }Copy the code
Fetcher this fetcher has many implementation classes. The network implementation class is HttpUrlFetcher, which calls loadData to loadData
HttpUrlFetcher.java
public void loadData(
@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
// Use HttpURLConnection by default for network requests
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0.null, glideUrl.getHeaders());
// The callback was successfully downloaded
callback.onDataReady(result);
} catch(IOException e) {...// Download failed
callback.onLoadFailed(e);
} finally{...}}Copy the code
Five picture download after successful callback
5.1 Successful Network Callback
SourceGenerator.java
private void startNextLoad(finalLoadData<? > toStart) {
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback<Object>() {
@Override
public void onDataReady(@Nullable Object data) {
if (isCurrentRequest(toStart)) {
// The network request is successfully callbackonDataReadyInternal(toStart, data); }}@Override
public void onLoadFailed(@NonNull Exception e) {
if(isCurrentRequest(toStart)) { onLoadFailedInternal(toStart, e); }}}); }Copy the code
SourceGenerator.java
void onDataReadyInternal(LoadData
loadData, Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if(data ! =null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// We might be being called back on someone else's thread. Before doing anything, we should
// reschedule to get back onto Glide's thread.
cb.reschedule();
} else {
// Continue calling the callbackcb.onDataFetcherReady( loadData.sourceKey, data, loadData.fetcher, loadData.fetcher.getDataSource(), originalKey); }}Copy the code
DecodeJob.java
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) {// Call back to Glide call thread
runReason = RunReason.DECODE_DATA;
// Callback This is what creates DecodeJob. It's EngineJob, callback
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
// The onResourceReady of the EngineJob is called
decodeFromRetrievedData();
} finally{ GlideTrace.endSection(); }}}Copy the code
DecodeJob.java
private void decodeFromRetrievedData(a) {··· Resource<R> Resource =null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if(resource ! =null) {
// Call EngineJob, callback
notifyEncodeAndRelease(resource, currentDataSource);
} else{ runGenerators(); }}Copy the code
DecodeJob.java
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
if (resource instanceofInitializable) { ((Initializable) resource).initialize(); }...// Call EngineJob, callbacknotifyComplete(result, dataSource); ...}Copy the code
At this point, the onResourceReady method that calls the EngineJob is invoked
DecodeJob.java
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
Copy the code
EngineJob.java
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
notifyCallbacksOfResult();
}
Copy the code
void notifyCallbacksOfResult(a) {...// Call the callback
engineJobListener.onEngineJobComplete(this, localKey, localResource);
for (final ResourceCallbackAndExecutor entry : copy) {
// Perform the callback
entry.executor.execute(new CallResourceReady(entry.cb));
}
decrementPendingCallbacks();
}
Copy the code
Execute the CallResourceReadyde run method
CallResourceReady.java
public void run(a) {
// Make sure we always acquire the request lock, then the EngineJob lock to avoid deadlock
// (b/136032534).
/ / lock
synchronized (cb.getLock()) {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
// Acquire for this particular callback.
// Add a referenceengineResource.acquire(); callCallbackOnResourceReady(cb); removeCallback(cb); } decrementPendingCallbacks(); }}}Copy the code
EngineJob.java
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 newCallbackException(t); }}Copy the code
Cb. OnResourceReady (engineResource, dataSource); The cb is final ResourceCallbacksAndExecutors CBS = new ResourceCallbacksAndExecutors (). He is a few storage monitored successfully callbacks and actuators, his assignment is waitForExistingOrStartNewJob in Engine
Engine.java
private <R> LoadStatus waitForExistingOrStartNewJob(
GlideContext glideContext,
Object model,
Key signature,
int width,
intheight, 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) {
// Add callback and actuatorcurrent.addCallback(cb, callbackExecutor); ...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);
// Add callback and actuatorengineJob.addCallback(cb, callbackExecutor); engineJob.start(decodeJob); ...return new LoadStatus(cb, engineJob);
}
Copy the code
This ResourceCallback type cb is passed in from Engine’s load method, engine.Load () is called onSizeReady() in SingleRequest, and onSizeReady implements ResourceCallback
SingleRequest.java
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized(requestLock) {... loadStatus = engine. The 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 is ResourceCallback
this, callbackExecutor); ...}}Copy the code
The SingleRequest onResourceReady
SingleRequest.java
public void onResourceReady(Resource
resource, DataSource dataSource) { stateVerifier.throwIfRecycled(); Resource<? > toRelease =null;
try {
synchronized (requestLock) {
loadStatus = null;
if (resource == null) {.../ / error
onLoadFailed(exception);
return;
}
Object received = resource.get();
if (received == null| |! TranscodeClass. IsAssignableFrom (received) getClass ())) {... 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;
}
// Call processing dataonResourceReady((Resource<R>) resource, (R) received, dataSource); }}finally {
if(toRelease ! =null) { engine.release(toRelease); }}}Copy the code
SingleRequest.java
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
// Status succeeded
status = Status.COMPLETE;
this.resource = resource;
···
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
if(requestListeners ! =null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
// The listener callback is completelistener.onResourceReady(result, model, target, dataSource, isFirstResource); } } anyListenerHandledUpdatingTarget |= targetListener ! =null
// The targetListener call succeeded
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if(! anyListenerHandledUpdatingTarget) { Transition<?super R> animation = animationFactory.build(dataSource, isFirstResource);
// Call target successfully. This target is the target we set externallytarget.onResourceReady(result, animation); }}finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
}
Copy the code
Six summarize
1. After the image is loaded, the image will be fetched from memory, and the image will have a counter marking the number of references. 2, the active cache is a Map
type, the current View is displayed, the memory cache is LruCache