Glide源码学习笔记二:load方法做了什么?

Glide源码学习笔记二:load方法做了什么?

前言

上文分析完Glide的第一步with(Context context),链接在此:Glide源码学习笔记一:with方法做了什么?,Glide在图片加载的界面添加了一个看不见的RequestManagerFragment,返回了一个与之绑定的RequestManager,接下来分析load(url)做的事情。

RequestManager

首先搞清楚RequestManager是做什么的,来看看类注释:

/**
 * A class for managing and starting requests for Glide. Can use activity, fragment and connectivity
 * lifecycle events to intelligently stop, start, and restart requests. Retrieve either by
 * instantiating a new object, or to take advantage built in Activity and Fragment lifecycle
 * handling, use the static Glide.load methods with your Fragment or Activity.
 */

简单翻译一下就是作为管理以及开始Glide请求的一个类。连接了activity,fragment的生命周期,可以更加灵活的停止,开始,重试图片加载请求。上一篇文章说的就是如何赋予RequestManager这样的能力。那RequestManager是如何实现这些功能的呢?

/**
   * Cancels any in progress loads, but does not clear resources of completed loads.
   * 取消当前RequestManager的加载请求但是不清除已经下载完成的资源
   */
  public synchronized void pauseRequests() {
    requestTracker.pauseRequests();
  }
  
  /**
   * Cancels any in progress loads and clears resources of completed loads.
   * 取消当前RequestManager的加载请求并且清除已经下载完成的资源
   */
  public synchronized void pauseAllRequests() {
    requestTracker.pauseAllRequests();
  }

  /**
   * Performs {@link #pauseAllRequests()} recursively for all managers that are contextually
   * descendant to this manager based on the Activity/Fragment hierarchy.
   * 递归暂停所有RequestManager的请求。
   */
  public synchronized void pauseAllRequestsRecursive() {
    pauseAllRequests();
    for (RequestManager requestManager : treeNode.getDescendants()) {
      requestManager.pauseAllRequests();
    }
  }
  ......
  ......
  省略大部分操作

以上对于请求的处理,都是加锁的,因此我们需要尽量避免这些方法的调用。而最终处理这些特殊情况的类是RequestTracker。这是一开始就被new出来的。来看看请求跟踪器是怎么处理这些请求的:

  //需要执行的请求,弱引用的hashMap作为容器,以防请求过多导致的OOM,再使用set去重
  private final Set<Request> requests =
      Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
  //特殊情况导致中断的请求,set去重
  private final Set<Request> pendingRequests = new HashSet<>();
  ......
  ......
  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");
      }
      //请求放入待处理的set中
      pendingRequests.add(request);
    }
  }
    public void pauseRequests() {
    isPaused = true;
    for (Request request : Util.getSnapshot(requests)) {
      if (request.isRunning()) {
        // Avoid clearing parts of requests that may have completed (thumbnails) to avoid blinking
        // in the UI, while still making sure that any in progress parts of requests are immediately
        // stopped.
        request.pause();
        pendingRequests.add(request);
      }
    }
  }
......
......

RequestTracker将请求分为两种,正常执行的请求和异常状态的请求,分别存放在两个set中,而RequestTracker是与RequestManager一一绑定,Glide使用这种方法将请求分类,统一处理。异常状态下将正常请求清除,释放内存,将请求加入到待处理的set中,以便在状态恢复正常时继续加载。

RequestManager.load(url)=>RequestBuilder

先看代码:

  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
    return asDrawable().load(bitmap);
  }
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
    return asDrawable().load(drawable);
  }
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Uri uri) {
    return asDrawable().load(uri);
  }
  .......
  .......

源码中重载方法有很多,只是传入对象有Drawable,String,BitMap等等。接下来看看asDrawable做了什么。

 @NonNull
  @CheckResult
  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }
  @NonNull
  @CheckResult
  public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }
  @NonNull
  @CheckResult
  public RequestBuilder<File> downloadOnly() {
    return as(File.class).apply(DOWNLOAD_ONLY_OPTIONS);
  }

都是为了构造出一个RequestBuilder对象,来看看这个RequestBuilder是做什么工作的:

RequestBuilder

/**
 * A generic class that can handle setting options and staring loads for generic resource types.
 * 一个可以处理加载选项以及开始加载通用资源类型的类。
 * 继承自BaseRequestOptions,这是一个封装了所有加载选项,也就是自带图片加载功能,例如缩略图,是否缓存,圆角等等。
 * 因此Glide将使用者多样化的加载请求参数都封装在该类中,对图片加载请求做定制化处理。
 */

大部分情况下加载图片都是使用ImageView作为容器显示的,因此大部分RequestBuilder的resourceClass都是Drawable.class,只有一种特殊情况,就是当只下载不显示时,是File.class。
接下来,看看RequestBuilder.load做了什么操作。

RequestBuilder.load(T t)=>RequestBuilder

同样,load方法根据传入参数不同,也有很多重载方法:

  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
    return loadGeneric(bitmap).apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
  }
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<TranscodeType> load(@Nullable Drawable drawable) {
    return loadGeneric(drawable).apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
  }
  @NonNull
  @Override
  @CheckResult
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
  ......
  ......

所有的重载方法要么返回的是loadGeneric,要么是追加了一个apply方法,loadGeneric返回的是RequestBuilder本身,apply方法也是一样,那么这两个方法对于RequestBuilder分别做了什么处理呢?

  @NonNull
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    //默认false,通过调用autoClone方法修改为true,是否需要深拷贝
    if (isAutoCloneEnabled()) {
      return clone().loadGeneric(model);
    }
    //可以理解为数据来源
    this.model = model;
    //标志位,在into方法校验,目的是不能在没有调用load方法的情况下调用into
    isModelSet = true;
    return selfOrThrowIfLocked();
  }
  @NonNull
  @SuppressWarnings("unchecked")
  protected final T selfOrThrowIfLocked() {
  //isLocked默认为false,可以调用lock方法更改为true
    if (isLocked) {
      throw new IllegalStateException("You cannot modify locked T, consider clone()");
    }
    return self();
  }

从代码中可以看出,只是设置了两个标志位,做了是否需要深拷贝以及是否被锁的判断,还算简单。那apply做了什么呢?

  public RequestBuilder<TranscodeType> apply(@NonNull BaseRequestOptions<?> requestOptions) {
    Preconditions.checkNotNull(requestOptions);
    return super.apply(requestOptions);
  }
public T apply(@NonNull BaseRequestOptions<?> o) {
    if (isAutoCloneEnabled) {
      return clone().apply(o);
    }
    BaseRequestOptions<?> other = o;

    if (isSet(other.fields, SIZE_MULTIPLIER)) {
      sizeMultiplier = other.sizeMultiplier;
    }
    if (isSet(other.fields, USE_UNLIMITED_SOURCE_GENERATORS_POOL)) {
      useUnlimitedSourceGeneratorsPool = other.useUnlimitedSourceGeneratorsPool;
    }
    //都是相似方法,校验当前属性是否和参数一致,不一致就重新赋值
  	......
  	......
  	......
    if (isSet(other.fields, ONLY_RETRIEVE_FROM_CACHE)) {
      onlyRetrieveFromCache = other.onlyRetrieveFromCache;
    }

    // Applying options with dontTransform() is expected to clear our transformations.
    if (!isTransformationAllowed) {
      transformations.clear();
      fields &= ~TRANSFORMATION;
      isTransformationRequired = false;
      fields &= ~TRANSFORMATION_REQUIRED;
      isScaleOnlyOrNoTransform = true;
    }

    fields |= other.fields;
    options.putAll(other.options);

    return selfOrThrowIfLocked();
  }
  
  private static boolean isSet(int fields, int flag) {
  //这里的设计很巧妙,阅读源码可以发现,已经定义的所有flag都是将1左移不同位,以一个int值存储,而fields默认为0,
  //设置一个属性就将对应位置为1,因此做一次&运算就可以知道是flag是否一致。
    return (fields & flag) != 0;
  }

通过代码得知,apply方法实际上就是做了一次巧妙的赋值,将新设置的属性赋值到当前的RequestBuilder中。

总结

本次涉及到的关键类之间的关系如下:
在这里插入图片描述
这一次的源码学习,弄清楚了一些之前理解不到的问题:
1.app在后台运行图片加载会暂停吗?再次前台运行怎么保证图片继续加载?
2.Glide是如何处理不同的缩略图,圆角属性,缓存模式?

对于位运算的运用,很是巧妙,对于日后处理相似场景很有帮助。
load方法不load,Glide到底怎么load?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值