In the blink of an eye graduation has been 3 years, but my technology growth seems not to keep up with the pace of time, while 51 vacation, decided to study Glide image loading framework analysis. The article intends to study Glide analysis from the following aspects.

preface

Glide is our project development is very common in a tripartite framework, its function is extremely powerful it for us to come out of a good image load cache and other functions. It’s very convenient to use. So how should we use it? What is its implementation?

Glide address: github.com/bumptech/gl…

Glide official use document: muyangmin. Making. IO/Glide – docs -…

Article Glide version: 4.9.0

This article mainly analyzes the flow of glide loading.

The basic use

  1. Simple loading

    / / the context makes the activity, application and fragments
    //imageUrl is the address of the image
    //image Specifies the image to be displayed
    Glide.with(context).load(imageUrl).into(image);
    Copy the code
  2. Set the placeholder map and the image that fails to load

    Glide.with(context)
    .load(imageUrl)
    .placeholder(R.drawable.coordinator_demo)
    .error(R.drawable.coordinator_demo)
    .into(image);
    Copy the code

    Above is our configuration for loading a single image, but it is common for many of our images to load the same except for the loading address and the ImageView that needs to be displayed. Do we have to set up every image to load? The answer is no, glide provides us with an object: RequestOptions to share image add-ins from different modules.

    val requestOptions = RequestOptions()
                requestOptions
                        .placeholder(R.drawable.coordinator_demo)
                        .error(R.drawable.coordinator_demo)
     Glide.with(context).load(imageUrl).apply(requestOptions).into(image);
    Copy the code

    Creating a RequestOptions object is simple. You can either simply create a new object or return a RequestOptions object by calling any of its static methods.

Glide source analysis

Through the use of Glide we found that glide image loading mainly uses three methods:

  • with()
  • load()
  • into()

With the method

You can see that the deprecated method Glide has five overridden with. All of their return objects are instances of RequestManager objects.

By reading the source code we can see that the final returned RequestManager object is returned by the RequestManagerRetriever’s GET method. Get () has five overloads corresponding to the five overloads of Glide. Let’s go through it one by one.

  1. The Context passed is the Context of the Application. The final call is to the getApplicationManager method. Its implementation is as follows:

    @NonNull
      private RequestManager getApplicationManager(@NonNull Context context) {
        // Either an application context or we're on a background thread. if (applicationManager == null) { synchronized (this) { if (applicationManager == null) { //  Normally pause/resume is taken care of by the fragment we add to the fragment or // activity. However, in this case since the manager attached to the application will not // receive lifecycle events, we must force the manager to start resumed using // ApplicationLifecycle. // TODO(b/27524013): Factor out this Glide.get() call. Glide glide = Glide.get(context.getApplicationContext()); applicationManager = factory.build( glide, new ApplicationLifecycle(), new EmptyRequestManagerTreeNode(), context.getApplicationContext()); } } } return applicationManager; }Copy the code

    The double-locked singleton ensures that there is only one RequestManager for the Application Context.

  2. The incoming object is FragmentActivity

     @NonNull
      public RequestManager get(@NonNull FragmentActivity activity) {
        if (Util.isOnBackgroundThread()) {
          return get(activity.getApplicationContext());
        } else {
          assertNotDestroyed(activity);
          FragmentManager fm = activity.getSupportFragmentManager();
          return supportFragmentGet(
              activity, fm, /*parentHint=*/ null, isActivityVisible(activity)); }}Copy the code

    As you can see, the above code eventually calls the supportFragmentGet method, which is where the RequestManager is actually returned.

    @NonNull
      private RequestManager supportFragmentGet(
          @NonNull Context context,
          @NonNull FragmentManager fm,
          @Nullable Fragment parentHint,
          boolean isParentVisible) {
        SupportRequestManagerFragment current =
            getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
          // TODO(b/27524013): Factor out this Glide.get() call.
          Glide glide = Glide.get(context);
          requestManager =
              factory.build(
                  glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
          current.setRequestManager(requestManager);
        }
        return requestManager;
      }
    Copy the code

    Because this article has a cursory look at Glide loading, we won’t go into the details of how the RequestManager is generated.

  3. The incoming object is Fragment

    @NonNull
      public RequestManager get(@NonNull Fragment fragment) {
        Preconditions.checkNotNull(fragment.getActivity(),
              "You cannot start a load on a fragment before it is attached or after it is destroyed");
        if (Util.isOnBackgroundThread()) {
          return get(fragment.getActivity().getApplicationContext());
        } else {
          FragmentManager fm = fragment.getChildFragmentManager();
          returnsupportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible()); }}Copy the code

    Again, supportFragmentGet is called to get the RequestManager object.

    We don’t need to get too detailed here except that Glide creates a requestManager based on the different activities or fragments that are passed in. And bind to its life cycle

load

Glide’s with method returns the RequestManager and then let’s look at the Load method of the RequestManager.

Each load calls the asDrawable method by default. Let’s take a look at its implementation:

 public RequestBuilder<Drawable> asDrawable(a) {
    return as(Drawable.class);
  }
  
   public <ResourceType> RequestBuilder<ResourceType> as( @NonNull Class
       
         resourceClass)
        {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }
Copy the code

You can see the RequestBuilder that returns the generic Drawable class. After the RequestBuilder object is acquired, its load method is called.

public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
Copy the code
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }
Copy the code

As you can see, the requestManager returns a RequestBuilder instance object. Load generates RequestBuilder instance objects by default, except when it actively calls the As method of the requestManager.

Both the RequestManager and RequestBuilder implement the ModelTypes interface. The RequestManager is used to generate RequestBuilder objects that are generically qualified. The RequestBuilder is used to configure the required parameters for the request image.

into

The RequestBuilder’s INTO has multiple overloaded methods, but they all end up calling the following method: This method takes four arguments

Target: Target object;

RequestListener: Image load listener

BaseRequestOptions: configuration items for loading images

Executor: The pool of threads that execute the image load

Here we do process analysis, not to do a detailed explanation of the source of each parameter.

private <Y extends Target<TranscodeType>> Y into( @NonNull Y target, @Nullable RequestListener
       
         targetListener, BaseRequestOptions
         options, Executor callbackExecutor)
        {
    Preconditions.checkNotNull(target);
    if(! isModelSet) {throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
// Build the Request object
    Request request = buildRequest(target, targetListener, options, callbackExecutor);

    Request previous = target.getRequest();
    if(request.isEquivalentTo(previous) && ! isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { request.recycle();// If the request is completed, beginning again will ensure the result is re-delivered,
      // triggering RequestListeners and Targets. If the request is failed, beginning again will
      // restart the request, giving it another chance to complete. If the request is already
      // running, we can let it continue running without interruption.
      if(! Preconditions.checkNotNull(previous).isRunning()) {// Use the previous request rather than the new one to allow for optimizations like skipping
        // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
        // that are done in the individual Request.
        previous.begin();
      }
      return target;
    }

    requestManager.clear(target);
    target.setRequest(request);
    // Initiate an image load request
    requestManager.track(target, request);

    return target;
  }
 
Copy the code

The code above does two things: build the Request object; Initiate an image load request.

The Request object is eventually built using the obtainRequest method without the thumbnail

 private Request obtainRequest( Target<TranscodeType> target, RequestListener<TranscodeType> targetListener, BaseRequestOptions<? > requestOptions, RequestCoordinator requestCoordinator, TransitionOptions<? ,?super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      Executor callbackExecutor) {
    return SingleRequest.obtain(
        context,
        glideContext,
        model,
        transcodeClass,
        requestOptions,
        overrideWidth,
        overrideHeight,
        priority,
        target,
        targetListener,
        requestListeners,
        requestCoordinator,
        glideContext.getEngine(),
        transitionOptions.getTransitionFactory(),
        callbackExecutor);
  }
Copy the code

That is, the final request is an instance of SingleRequest, which is a generic class. The R here is the same as the RequestBuilder generics. RequestBuilder corresponds to SingleRequest.

Network request initiation: requsetManager track method:

 synchronized void track(@NonNull Target
        target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }
Copy the code

We know from Glide that each requestManager has an activity or fragment that has a lifecycle bound to it: the requestManager then assigns the lifecycle of the corresponding request to the RequestTracker to process.

Let’s take a look at RequestTracker’s runRequest method:

/** * Starts tracking the given request. */
  public void runRequest(@NonNull Request request) {
    requests.add(request);
    if(! isPaused) { request.begin(); }else {
      request.clear();
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request"); } pendingRequests.add(request); }}Copy the code

As you can see here, if the current state is paused, add the request to a list to be executed until the next time it can be executed, and call begin() on the request from the non-paused state to load it directly.

We know that the request is a SingleRequest object. Let’s look at its begin method:

@Override
  public synchronized void begin(a) {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    if (model == null) {
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        width = overrideWidth;
        height = overrideHeight;
      }
      int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
      onLoadFailed(new GlideException("Received null model"), logLevel);
      return;
    }

    if (status == Status.RUNNING) {
      throw new IllegalArgumentException("Cannot restart a running request");
    }
    if (status == Status.COMPLETE) {
      onResourceReady(resource, DataSource.MEMORY_CACHE);
      return;
    }
    status = Status.WAITING_FOR_SIZE;
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      onSizeReady(overrideWidth, overrideHeight);
    } else {
      target.getSize(this);
    }

    if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
        && canNotifyStatusChanged()) {
      target.onLoadStarted(getPlaceholderDrawable());
    }
    if (IS_VERBOSE_LOGGABLE) {
      logV("finished run method in "+ LogTime.getElapsedMillis(startTime)); }}Copy the code

This method is a bit long, but the logic is simple. The following points need to be noted:

  1. The direct callback load fails if the Model is empty, where the Model is the object we pass when we call the Load method of the RequestManager.
  2. If the current width and height are available, call onSizeReady directly for loading, otherwise register the listener with targetgetSize(SizeReadyCallback CB), and call onSizeReady when the target width and height are available. Load.
  3. Target.onloadstarted () is called if the current state is running or waiting for size to be prepared, where we set the placeholder map to display.

Let’s take a look at the onSizeReady loading implementation: its core is just one line: call engine’s load method to load.

Let’s take a look at engine’s Load implementation:

public synchronized <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);
// Load from the active resourceEngineResource<? > active = loadFromActiveResources(key, isMemoryCacheable);if(active ! =null) {
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      return null;
    }
// Load from the cacheEngineResource<? > cached = loadFromCache(key, isMemoryCacheable);if(cached ! =null) {
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return null; } EngineJob<? > current = jobs.get(key, onlyRetrieveFromCache);// If a key is loaded asynchronously, the same load is already in progress
    if(current ! =null) {
      current.addCallback(cb, callbackExecutor);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }
// Start the thread for asynchronous loading
    EngineJob<R> engineJob =
        engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);

    // Note that R is determined by transcodeClass. This value is qualified by R of SingleRequest
      
    SingleRequest
      
        has the same TranscodeType as RequestBuilder
       
        .
       
    // It is also a Drawable object
    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);

    engineJob.addCallback(cb, callbackExecutor);
    engineJob.start(decodeJob);

    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);
  }
Copy the code

From the above flow you can see that Glide image loading has three sources:

  1. Activity resources
  2. Memory cache
  3. File

Start (decodeJob) is a runnable object. Let’s look directly at its run method. Its core code is just one line: call runWrapped (). Let’s look at runWrapped and its related implementation

 private void runWrapped(a) {
     // Get the state based on runReason
    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); }}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); }}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); }}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(); }// Otherwise a generator started a new load and we expect to be called back in
    // onDataFetcherReady.
  }
Copy the code

The above code logic is complicated and won’t be explained here, but what it basically means is that a runGenerators call to startNext () of the DataFetcherGenerator will first read the image resource (resource type and data source) from the file. If it’s not there we’re loading from the network. According to the introduction of official documentation glide cache is divided into 4 levels

  1. Active Resources – Is there another View showing this image now?
  2. Memory cache – Is the image recently loaded and still in Memory?
  3. Resource Type – Has the image been decoded, converted, and written to the disk cache before? Corresponding ResourceCacheGenerator
  4. Data Source – Has the resource from which this image was built been previously written to the file cache? Corresponding DataCacheGenerator

From the previous process analysis, we know that level 1 and 2 caches are called in the Engine class load, and level 3 and 4 caches are called in the DecodeJob. We do not analyze cached calls here. Direct access to network on – in analysis.

In the runGenerators() method when stage == stage.source exits the loop and calls reschedule() to execute the run method of the DecodeJob

@Override
  public void reschedule(a) {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);
  }
Copy the code

We know from previous knowledge that the startNext() method of the SourceGenerator is executed in the runGenerators method. It is implemented as follows

public boolean startNext(a) {
    if(dataToCache ! =null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

    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;
        loadData.fetcher.loadData(helper.getPriority(), this); }}return started;
  }
Copy the code

With debug we discover that the fetcher object of loadData is an inner class of MultiModelLoader when model is the image network address.

The final call to HttpUrlFetcher, with the image network address passed in, does the image loading. When data preparation is complete, call back to the onDataReady method of the SourceGenerator. If no caching is required, the data preparation is notified directly. Otherwise, the data preparation is notified by caching first.

@Override
  public void onDataReady(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{ cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher, loadData.fetcher.getDataSource(), originalKey); }}Copy the code

Eventually they all call the onDataFetcherReady () method of the DecodeJob, which in turn calls the decodeFromRetrievedData ()

DecodeFromRetrievedData Calls notifyEncodeAndRelease() when the resource is ready to notify that the data is ready.

@Override
  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) { runReason = RunReason.DECODE_DATA; callback.reschedule(this);
    } else {
      GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
      try {
        decodeFromRetrievedData();
      } finally{ GlideTrace.endSection(); }}}private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    if (resource instanceof Initializable) {
      ((Initializable) resource).initialize();
    }

    Resource<R> result = resource;
    LockedResource<R> lockedResource = null;
    if (deferredEncodeManager.hasResourceToEncode()) {
      lockedResource = LockedResource.obtain(resource);
      result = lockedResource;
    }

    notifyComplete(result, dataSource);

    stage = Stage.ENCODE;
    try {
      if(deferredEncodeManager.hasResourceToEncode()) { deferredEncodeManager.encode(diskCacheProvider, options); }}finally {
      if(lockedResource ! =null) { lockedResource.unlock(); }}// Call onEncodeComplete outside the finally block so that it's not called if the encode process
    // throws.
    onEncodeComplete();
  }
Copy the code
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
    callback.onResourceReady(resource, dataSource);
  }
Copy the code

Notice that the notifyComplete callback is the EngineJob passed in to create the DecodeJob object, so the EngineJob’s onResourceReady () is called. Let’s look at his implementation of the onResourceReady method

@Override
  public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    synchronized (this) {
      this.resource = resource;
      this.dataSource = dataSource;
    }
    notifyCallbacksOfResult();
  }
  
   @Synthetic
  void notifyCallbacksOfResult(a) { ResourceCallbacksAndExecutors copy; Key localKey; EngineResource<? > localResource;synchronized (this) {
      stateVerifier.throwIfRecycled();
      if (isCancelled) {
        // TODO: Seems like we might as well put this in the memory cache instead of just recycling
        // it since we've gotten this far...
        resource.recycle();
        release();
        return;
      } else if (cbs.isEmpty()) {
        throw new IllegalStateException("Received a resource without any callbacks to notify");
      } else if (hasResource) {
        throw new IllegalStateException("Already have resource");
      }
      engineResource = engineResourceFactory.build(resource, isCacheable);
      // Hold on to resource for duration of our callbacks below so we don't recycle it in the
      // middle of notifying if it synchronously released by one of the callbacks. Acquire it under
      // a lock here so that any newly added callback that executes before the next locked section
      // below can't recycle the resource before we call the callbacks.
      hasResource = true;
      copy = cbs.copy();
      incrementPendingCallbacks(copy.size() + 1);

      localKey = key;
      localResource = engineResource;
    }
// This will add the image information to the activity resource,
    listener.onEngineJobComplete(this, localKey, localResource);

      // The for loop finally executes the onResourceReady method of SingleRequest
    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
    decrementPendingCallbacks();
  }
Copy the code

SingleRequest’s onResourceReady() will eventually execute the following code, ignoring other conditions:

target.onResourceReady(result, animation);
Copy the code

Let’s assume that when the image is loaded into is passed an ImageView. We did not analyze how the target was built in the previous section, but let us add a note:

 public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    // Omit other code
    return into(
        /** Build the target object */
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }
Copy the code

Target object through glideContext. BuildImageViewTarget (view, transcodeClass) to build: we see glideContext corresponding implementation:

 @NonNull
  public <X> ViewTarget<ImageView, X> buildImageViewTarget( @NonNull ImageView imageView, @NonNull Class
       
         transcodeClass)
        {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }
Copy the code
public class ImageViewTargetFactory {
  @NonNull
  @SuppressWarnings("unchecked")
  public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view, @NonNull Class
       
         clazz)
        {
    if (Bitmap.class.equals(clazz)) {
      return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
    } else if (Drawable.class.isAssignableFrom(clazz)) {
      return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
    } else {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)"); }}}Copy the code

If the RequestBuilder’s transcodeClass is a Bitmap then the target is a BitmapImageViewTarget, If it’s a subclass of Drawable then target is a DrawableImageViewTarget.

We select DrawableImageViewTarget to view: Since DrawableImageViewTarget does not have an onResourceReady method we view it in its parent ImageViewTarget

@Override
  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null| |! transition.transition(resource,this)) {
      setResourceInternal(resource);
    } else{ maybeUpdateAnimatable(resource); }}Copy the code

DrawableImageViewTarget: You can see that its setResource method sets the load image for the ImageView

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {

  public DrawableImageViewTarget(ImageView view) {
    super(view);
  }

  / * * *@deprecated Use {@link #waitForLayout()} instead.
   */
  // Public API.
  @SuppressWarnings({"unused"."deprecation"})
  @Deprecated
  public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {
    super(view, waitForLayout);
  }

  @Override
  protected void setResource(@Nullable Drawable resource) { view.setImageDrawable(resource); }}Copy the code

Summary:

Here the glide image loading process is analyzed. Due to the length of the glide loading process we carry out a simple review.

Glide loading timing diagram: