The entry method is enigine.load ()
- Loading from memory — loadFromMemory()
- Load from disk or network
public <R> LoadStatus load(/ * * * /) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
EngineKey key =
keyFactory.buildKey(/ / * * *);EngineResource<? > memoryResource;synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
return waitForExistingOrStartNewJob(
/ / * * *)
}
}
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
Copy the code
Memory loading process:
- Load from a resource being used in memory
- Load from memory cache
privateEngineResource<? > loadFromMemory( EngineKey key,boolean isMemoryCacheable, long startTime) {
if(! isMemoryCacheable) {return null; } EngineResource<? > active = loadFromActiveResources(key);if(active ! =null) {
returnactive; } EngineResource<? > cached = loadFromCache(key);if(cached ! =null) {
return cached;
}
return null;
}
Copy the code
Load from a resource in use:
ActivityResources is a resource management class that saves the resources that are currently being used (holding the resources with WeakReference and saving them in a Map).
privateEngineResource<? > loadFromActiveResources(Key key) { EngineResource<? > active = activeResources.get(key);if(active ! =null) {
active.acquire();
}
return active;
}
Copy the code
Loading from cache:
Get the cache from MemoryCache and, if any, add one to the resource reference count and add it to ActivityResources.
privateEngineResource<? > loadFromCache(Key key) { EngineResource<? > cached = getEngineResourceFromCache(key);if(cached ! =null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
privateEngineResource<? > getEngineResourceFromCache(Key key) { Resource<? > cached = cache.remove(key);finalEngineResource<? > 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
If not in memory, need from the disk cache or the network load, the main logic in waitForExistingOrStartNewJob () :
- Check whether there is a loading task for the resource. If yes, add a callback to avoid repeated loading.
- Start a EngineJob callback that manages the loading, and the actual loading is done in the DecodeJob
private <R> LoadStatus waitForExistingOrStartNewJob(/ * * /) { EngineJob<? > current = jobs.get(key, onlyRetrieveFromCache);if(current ! =null) {
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(/ * * /); DecodeJob
decodeJob = decodeJobFactory.build(/**/
);
jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
Copy the code
Take a look at the implementation of enginejob.start (), which puts DecodeJob into a thread pool for execution:
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
Copy the code
DecodeJob implements the Runnable interface, focusing on the run() method it implements:
- Check whether it has been canceled
- Call the runWrapped() method to perform the state transition
- Some exception handling and callback execution
public void run(a) {
/ / * * *DataFetcher<? > localFetcher = currentFetcher;try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (CallbackException e) {
/ / * * *
throw e;
} catch (Throwable t) {
/ / * * *
if(stage ! = Stage.ENCODE) { throwables.add(t); notifyFailed(); }if(! isCancelled) {throw t;
}
throw t;
} finally {
/ / * * *}}Copy the code
RunWrapped () is a key method of state transition that handles initialization — loading — decoding:
- Judge the current state
- Get the resource handler
- Running the resource handler
private void runWrapped(a) {
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); }}Copy the code
The state transition relationship is in the getNextStage() method (which is related to disk caching policies) :
- RESOURCE_CACHE – Caches converted image resources
- DATA_CACHE – Only raw image resources are cached
- SOURCE — Not cached, only loaded from the network
- FINISHED — Only loaded from cache, state becomes FINISHED; Or the next state loaded from the network also ends
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); }}Copy the code
Get the data capture generator that should be used at this time by judging the current state:
- RESOURCE_CACHE corresponds to ResourceCacheGenerator(), which is used to get the cached transformed picture
- DATA_CACHE Corresponds to the original image resource that the DataCacheGenerator() user obtains from the cache
- SOURCE corresponds to SourceGenerator() for loading images from the network
- They both hold the FetcherReadyCallback interface implemented by DecodeJob, which handles the callback after the resource has loaded
private DataFetcherGenerator getNextGenerator(a) {
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); }}Copy the code
DecodeJob implements FetcherReadyCallback:
@Override
public void reschedule(a) {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
Copy the code
Take a look at the comment for RunReason:
private enum RunReason {
/** The first time we've been submitted. */
INITIALIZE,
/** We want to switch from the disk cache service to the source executor. */
SWITCH_TO_SOURCE_SERVICE,
/** * We retrieved some data on a thread we don't own and want to switch back to our thread to * process the data. */
DECODE_DATA,
}
Copy the code
SWITCH_TO_SOURCE_SERVICE indicates conversion from disk caching to data processing.
DecodeJob DecodeJob DecodeJob DecodeJob DecodeJob DecodeJob DecodeJob
@Override
public void reschedule(DecodeJob
job) {
// Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself
// up.
getActiveSourceExecutor().execute(job);
}
Copy the code
DecodeJob’s run() method will continue to call runWrapped(), and since runReason has changed to SWITCH_TO_SOURCE, it will continue to call runGenerators().
private void runGenerators(a) {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while(! isCancelled && currentGenerator ! =null
&& !(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();
}
}
Copy the code
(runGenerators() is a state machine that fetches image resources level by level. State transitions are implemented by getNextStage(), where the state is defined:
/** Where we're trying to decode data from. */
private enum Stage {
/** The initial stage. */
INITIALIZE,
/** Decode from a cached resource. */
RESOURCE_CACHE,
/** Decode from cached source data. */
DATA_CACHE,
/** Decode from retrieved source. */
SOURCE,
/** Encoding transformed resources after a successful load. */
ENCODE,
/** No more viable stages. */
FINISHED,
}
Copy the code
So if the resource is found in the RESOURCE_CACHE and DATA_CACHE caches end, otherwise a network request is made.