DecodeJob implements DataFetcherGenerator. FetcherReadyCallback interface:
interface FetcherReadyCallback {
//Glide reschedules the process in Glide's own thread
void reschedule(a);
// Data is successfully obtained
void onDataFetcherReady(
Key sourceKey,
@NullableObject data, DataFetcher<? > fetcher, DataSource dataSource, Key attemptedKey);
// Failed to obtain data
void onDataFetcherFailed( Key attemptedKey, Exception e, DataFetcher
fetcher, DataSource dataSource);
}
Copy the code
OnDataFetcherFailed Handling logic for data acquisition failure:
public void onDataFetcherFailed( Key attemptedKey, Exception e, DataFetcher
fetcher, DataSource dataSource) {
fetcher.cleanup();
GlideException exception = new GlideException("Fetching data failed", e);
exception.setLoggingDetails(attemptedKey, dataSource, fetcher.getDataClass());
throwables.add(exception);
// The thread is not the same, modify runReason, reschedule, end up calling runGenerators()
if(Thread.currentThread() ! = currentThread) { runReason = RunReason.SWITCH_TO_SOURCE_SERVICE; callback.reschedule(this);
} else{ runGenerators(); }}Copy the code
Therefore, if data retrieval fails, runGenerators() will continue to be called, using the following logic:
private void runGenerators(a) {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while(! isCancelled && currentGenerator ! =null
// The current generator starts loading data
&& !(isStarted = currentGenerator.startNext())) {
// Next state
stage = getNextStage(stage);
// According to the state, get the data loader
currentGenerator = getNextGenerator();
// Reschedule, switch to Glide's own thread
if (stage == Stage.SOURCE) {
reschedule();
return; }}if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
}
Copy the code
-
Continue to call the current DataFetcherGenerator. StartNext (), because ResourceCacheGenerator and DataCacheGenerator serial number counting, A previously tried modelloader.loadData call will not be repeated. If no suitable modelLoader.loadData is found, false will be returned
-
Call getNextStage() to get the next state:
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; default: throw new IllegalArgumentException("Unrecognized stage: "+ current); }}Copy the code
-
If neither RESOURCE_CACHE nor DATA_CACHE is found, SourceGenerator is called
-
If no SOURCE is available, the request fails and notifyFailed()
Reschedule rescheduling:
public void reschedule(a) {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
Copy the code
Rescheduling only happens when the data is loaded, so set runReason to runreason.switch_to_source_service and call the reschedule method for callback, This callback is implemented by EngineJob (the DecodeJob logic is in Engine and uses pools.pool).
Reschedule method for EngineJob:
public void reschedule(DecodeJob
job) {
getActiveSourceExecutor().execute(job);
}
Copy the code
So the DecodeJob is thrown into the thread pool and executed again. Finally, the DecodeJob’s run method is called. After checking the status, the runWrapped method is called. Because of the runReason value, the runGenerators method is eventually called to load the data.
OnDataFetcherReady Data loaded successfully:
public void onDataFetcherReady( Key sourceKey, Object data, DataFetcher
fetcher, DataSource dataSource, Key attemptedKey) {
// Save variables
this.currentSourceKey = sourceKey;
this.currentData = data;
this.currentFetcher = fetcher;
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
if(Thread.currentThread() ! = currentThread) {// Put it back into the thread pool and run->runWrapped->decodeFromRetrievedData
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally{ GlideTrace.endSection(); }}}Copy the code
Either reschedule rescheduling or processing directly, the decodeFromRetrievedData method is eventually called to decode:
private void decodeFromRetrievedData(a) {
Resource<R> resource = null;
try {
/ / decoding
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if(resource ! =null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
// Decoding failed, reloading data, the process has been analyzed aboverunGenerators(); }}Copy the code
DecodeFromData data decoding:
private <Data> Resource<R> decodeFromData( DataFetcher
fetcher, Data data, DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
long startTime = LogTime.getLogTime();
Resource<R> result = decodeFromFetcher(data, dataSource);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded result " + result, startTime);
}
return result;
} finally{ fetcher.cleanup(); }}private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
// Get the decoder based on the data type, e.g. HttpUrlFetcher results in InputStreamLoadPath<Data, ? , R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());return runLoadPath(data, dataSource, path);
}
private <Data, ResourceType> Resource<R> runLoadPath( Data data, DataSource dataSource, LoadPath
path)
,>
throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally{ rewinder.cleanup(); }}Copy the code
As you can see, the decoding is finally done by LoadPath. The result of decoding is set to the dataSource variable via the inner class DecodeCallback callback, and the onResourceDecoded method of DecodeJob is called.
Decoding complete 1
<Z> Resource<Z> onResourceDecoded(DataSource dataSource, @NonNull Resource<Z> decoded) {
Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
Transformation<Z> appliedTransformation = null;
Resource<Z> transformed = decoded;
// If it is not the transformed data cache, the set Transformation is used first
if(dataSource ! = DataSource.RESOURCE_DISK_CACHE) { appliedTransformation = decodeHelper.getTransformation(resourceSubClass); transformed = appliedTransformation.transform(glideContext, decoded, width, height); }// Recycle data directly to save memory, or put it in cache for future use
if(! decoded.equals(transformed)) { decoded.recycle(); }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;
booleanisFromAlternateCacheKey = ! decodeHelper.isSourceKey(currentSourceKey);// Cache converted data, because the data has been decoded, so cache in the file, need to be encoded again
if (diskCacheStrategy.isResourceCacheable(
isFromAlternateCacheKey, dataSource, encodeStrategy)) {
if (encoder == null) {
throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
}
final Key key;
switch (encodeStrategy) {
//Gif, defined in GifDrawableEncoder
case SOURCE:
key = new DataCacheKey(currentSourceKey, signature);
break;
case TRANSFORMED:
//Bitmap, defined in BitmapEncoder
key =
new ResourceCacheKey(
decodeHelper.getArrayPool(),
currentSourceKey,
signature,
width,
height,
appliedTransformation,
resourceSubClass,
options);
break;
default:
throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
}
// The parameters are saved to the deferredEncodeManager, and encode is performed in the next step
LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
deferredEncodeManager.init(key, encoder, lockedResult);
result = lockedResult;
}
return result;
}
Copy the code
You can see that after the decoding is complete, Transformation is used and the result of the Transformation is cached. (I feel that there will be duplication of processing here, for example, the data itself is loaded with transformed data, and then it needs to be encoded and cached into a file.)
Decoding complete 2
NotifyEncodeAndRelease is called when this is done:
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
Resource<R> result = resource;
LockedResource<R> lockedResource = null;
// Since decoding is synchronous, onResourceDecoded sets deferredEncodeManager
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
// Distribute the results
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
// Re-encode the transcoded data and cache it in a file, mainly in DiskLruCacheWrapperdeferredEncodeManager.encode(diskCacheProvider, options); }}finally {
if(lockedResource ! =null) { lockedResource.unlock(); }}// When everything is done, recycle resources and empty variables to improve gc efficiency
onEncodeComplete();
}
Copy the code