Glide (II) Request queue management

Posted by Pyrite on Sun, 31 May 2020 08:21:07 +0200

entrance

The first part summarizes the life cycle management of Glide by using fm and an empty Fragment. Here we continue to see load & into. Don't talk about it

// The first part analyzes with and continues to see load & into
Glide.with(this).load("url").into(...);
  • 1.1 load
public RequestBuilder<Drawable> load(@Nullable Object model) {
    return asDrawable().load(model);
  }

  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }

  // Instance a RequestBuilder
  public <ResourceType> RequestBuilder<ResourceType> as(Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass);
  }

  // requestManager in RequestBuilder construction
   protected RequestBuilder(Glide glide, RequestManager requestManager,
      Class<TranscodeType> transcodeClass) {
    this.glide = glide;
    this.requestManager = requestManager;
    this.context = glide.getGlideContext();
    this.transcodeClass = transcodeClass;
    this.defaultRequestOptions = requestManager.getDefaultRequestOptions();
    this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
    this.requestOptions = defaultRequestOptions;
  }
  • 2.1 into
public Target<TranscodeType> into(ImageView view) {
    // Main thread verification (involving View refresh)
    Util.assertMainThread();
    // View non empty verification
    Preconditions.checkNotNull(view);

    RequestOptions requestOptions = this.requestOptions;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
     // Get picture location type
      switch (view.getScaleType()) {
        case CENTER_CROP:
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
        case FIT_XY:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }

    return into(context.buildImageViewTarget(view, transcodeClass), requestOptions);
  }

  // Continue to see into
  private <Y extends Target<TranscodeType>> Y into(@NonNull Y target, RequestOptions options) {
    Util.assertMainThread();
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }

    options = options.autoClone();
    // build a Request according to the attribute in target & Options
    Request request = buildRequest(target, options);

    // target is bound to Request. The previous Request is reused here
    Request previous = target.getRequest();

    // Here, check whether the request is consistent with previous
    //Recycle the request uniformly
    if (request.isEquivalentTo(previous)) {
      request.recycle();

      //Here is to verify the previous execution.
      //begin internal mechanism:
      //If it is running, it will ensure no interruption
      //Restart and submit if complete
      //If it fails, start again
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        previous.begin();
      }
      return target;
    }

    // If the two requests are inconsistent, delete the old one, update the tartget in the request manager, and request SR to set
    requestManager.clear(target);
    target.setRequest(request);

    // requestManager add request to RequestTracker set < request >
    requestManager.track(target, request);

    return target;
  }
  • 2.2 it is not hard to see that the new request is added to the RequestTracker through the requestManager, which maintains a RequestTracker and can handle all requests according to the life cycle
// In the lifecycle callback, the corresponding targetTracker is processed differently
@Override
  public void onStart() {
    resumeRequests();
    targetTracker.onStart();
  }

  @Override
  public void onStop() {
    pauseRequests();
    targetTracker.onStop();
  }

  @Override
  public void onDestroy() {
    targetTracker.onDestroy();
    for (Target<?> target : targetTracker.getAll()) {
      clear(target);
    }
    targetTracker.clear();
    requestTracker.clearRequests();
    lifecycle.removeListener(this);
    lifecycle.removeListener(connectivityMonitor);
    mainHandler.removeCallbacks(addSelfToLifecycle);
    glide.unregisterRequestManager(this);
  }
  • 3.1 in RequestManager, requestTracker is created and initialized as a member variable of RequestManager. All requests are added to requestTracker by RequestManager for management and are linked with life cycle. Take a look at requestTracker

package com.bumptech.glide.manager;

import com.bumptech.glide.request.Request;
import com.bumptech.glide.util.Util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.WeakHashMap;

public class RequestTracker {
  // Use set to prevent GC from killing before starting or onPause
  private final Set<Request> requests =
      Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());

  @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
  private final List<Request> pendingRequests = new ArrayList<>();
  private boolean isPaused;

  /**
   * Starts tracking the given request.
   */
  public void runRequest(Request request) {
    requests.add(request);
    if (!isPaused) {
      request.begin();
    } else {
      pendingRequests.add(request);
    }
  }

  // Visible for testing.
  void addRequest(Request request) {
    requests.add(request);
  }

  /**
   * Stops tracking the given request, clears, and recycles it, and returns {@code true} if the
   * request was removed or {@code false} if the request was not found.
   */
  public boolean clearRemoveAndRecycle(Request request) {
    if (request == null) {
      return false;
    }
    boolean isOwnedByUs = requests.remove(request);
    // Avoid short circuiting.
    isOwnedByUs = pendingRequests.remove(request) || isOwnedByUs;
    if (isOwnedByUs) {
      request.clear();
      request.recycle();
    }
    return isOwnedByUs;
  }

  /**
   * Returns {@code true} if requests are currently paused, and {@code false} otherwise.
   */
  public boolean isPaused() {
    return isPaused;
  }

  /**
   * Stops any in progress requests.
   */
  public void pauseRequests() {
    isPaused = true;
    for (Request request : Util.getSnapshot(requests)) {
      if (request.isRunning()) {
        request.pause();
        pendingRequests.add(request);
      }
    }
  }

  /**
   * Starts any not yet completed or failed requests.
   */
  public void resumeRequests() {
    isPaused = false;
    for (Request request : Util.getSnapshot(requests)) {
      if (!request.isComplete() && !request.isCancelled() && !request.isRunning()) {
        request.begin();
      }
    }
    pendingRequests.clear();
  }

  /**
   * Cancels all requests and clears their resources.
   *
   * <p>After this call requests cannot be restarted.
   */
  public void clearRequests() {
    for (Request request : Util.getSnapshot(requests)) {
      clearRemoveAndRecycle(request);
    }
    pendingRequests.clear();
  }

  /**
   * Restarts failed requests and cancels and restarts in progress requests.
   */
  public void restartRequests() {
    for (Request request : Util.getSnapshot(requests)) {
      if (!request.isComplete() && !request.isCancelled()) {
        // Ensure the request will be restarted in onResume.
        request.pause();
        if (!isPaused) {
          request.begin();
        } else {
          pendingRequests.add(request);
        }
      }
    }
  }

  @Override
  public String toString() {
    return super.toString() + "{numRequests=" + requests.size() + ", isPaused=" + isPaused + "}";
  }
}
  • 3.2 as can be seen above, RequestTracker maintains the corresponding life cycle of all Requst

      1. For example, when onPause, pause all requests and add set
      1. When onResume, restart all requests
      1. clear all Request when onStop
  • A wave of 666

summary

  • It is mainly used for queue maintenance of all requests and corresponding optimization in life cycle callback
  • It's more direct to look at the picture

Topics: Java Fragment Attribute