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
,weakreference>