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
  1. 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

  2. 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
  3. If neither RESOURCE_CACHE nor DATA_CACHE is found, SourceGenerator is called

  4. 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