Android开发知识(二十七)Glide4.x的源码解析(上)

前言

    目前来说项目里用到的图片加载框架,都是Glide、Picasso、Fresco这三大图片框架其中的一种,记得以前还有个Universal-Image-Loader,现在一些老项目可能还有在使用。这几个框架都有各自的优缺点,而本篇要探索的重点是对Glide的源码解析,毕竟是google的亲儿子,Glide还是值得我们去使用和探索的。接下来我们不会赘述Glide的用法,重点放在源码分析上,由于源码也不少,所以分析的流程会沿着Glide.with(context).load(“http://xxx.png”).into(imageview)这条链路去分析。
    由于篇幅较长,这里我拆分成了上篇和下篇,下篇是:Glide4.x的源码解析(下).

获取RequestManager过程

    使用glide来加载图片那第一个方法我们就是Glide.with()了,它支持传入Activity、Context、Fragment、View这几种对象,来得到一个RequestManager对象。那么参数传入的类型不同,都有什么差别呢?
我们展开来说:
先从Glide.with(activity)入手,有两个方法:

 @NonNull
  public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
  }
    @NonNull
  public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
  }

(其实这两个方法本质上是一样的,只不过FragmentActivity是来自support的,后面就我们以FragmentActivity对象参数为例。)
    先看看getRetriever(activity)得到了啥:

  @NonNull
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // Context could be null for other reasons (ie the user passes in null), but in practice it will
    // only occur due to errors with the Fragment lifecycle.
    Preconditions.checkNotNull(
        context,
        "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
            + "returns null (which usually occurs when getActivity() is called before the Fragment "
            + "is attached or after the Fragment is destroyed).");
    return Glide.get(context).getRequestManagerRetriever();
  }

可以看到是得到了一个RequestManagerRetriever对象,来看下这个对象怎么拿到的:
Glide.get(context)代码:

 @NonNull
  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      GeneratedAppGlideModule annotationGeneratedModule =
          getAnnotationGeneratedGlideModules(context.getApplicationContext());
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context, annotationGeneratedModule);
        }
      }
    }

    return glide;
  }

    这里可以看出,这里用了单例设计模式-双重检查锁来得到一个全局的Glide对象,并不会创建多个。最终是通过Glide#initializeGlide方法来完成初始化的:

  private static void initializeGlide(
      @NonNull Context context,
      @NonNull GlideBuilder builder,
      @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
	  ...
	  //如果开启了AndroidManifest配置方式
	 if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
      manifestModules = new ManifestParser(applicationContext).parse();
    }
      ...
      builder.setRequestManagerFactory(factory);
      //解析在AndroidManifest中配置的module
      for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.applyOptions(applicationContext, builder);
    }
    //在注解类中回调applyOptions,我们可以在这里做default options的处理
     if (annotationGeneratedModule != null) {
      annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
    //创建了Glide对象,初始化
     Glide glide = builder.build(applicationContext);
	 ..	 
	 //向application注册了callback,glide类本身实现了ComponentCallbacks2接口,由于在内存不足时做操作,这个后面再细讲
 	applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
}

    在4.0以前我们在AndroidManifest中配置glide,而4.0后通过@GlideModule注解简化了这个,不过同时也保留了这个方式。并提供了一个isManifestParsingEnabled()方法来让我们决定用哪种方式。最后通过 builder.build(applicationContext)创建了Glide对象,也就是在GlideBuilder的build方法中,初始化各种池、加载引擎等,这个我们不展开讲了。值得注意的是在build方法中也创建了一个RequestManagerRetriever对象传入给Glide对象持有。
草草的说完Glide对象的创建,回到getRetriever方法可以看出就是从Glide对象中获取在Glide创建时也创建的RequestManagerRetriever对象。
那么RequestManagerRetriever这个对象是干嘛用的呢?我感觉要随着后面的分析才能逐渐说明它的用途。
    好,回到最上面的Glide#with方法,我们得知 getRetriever(activity)就是拿到RequestManagerRetriever对象(而如果是第一次拿则会初始化一个单例的Glide对象)。
而getRetriever(activity).get(activity),我们来看下这个get方法:

  @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));
    }
  }

    首先通过Util.isOnBackgroundThread()判断是否处于子线程,如果是子线程则调用get方法的另外一个重载方法:

 @NonNull
  public RequestManager get(@NonNull Context context) {
    if (context == null) {
      throw new IllegalArgumentException("You cannot start a load on a null Context");
    } else if (Util.isOnMainThread() && !(context instanceof Application)) {
      if (context instanceof FragmentActivity) {
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        return get((Activity) context);
      } else if (context instanceof ContextWrapper
          // Only unwrap a ContextWrapper if the baseContext has a non-null application context.
          // Context#createPackageContext may return a Context without an Application instance,
          // in which case a ContextWrapper may be used to attach one.
          && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
        return get(((ContextWrapper) context).getBaseContext());
      }
    }

    return getApplicationManager(context);
  }

    这个方法逻辑很清晰,前面我们说了Glide#with方法能接收Context对象,那么实际上Context对象就在这里做了类型区分。
而如果是子线程的话,容易看出就是走了最后那句代码: return getApplicationManager(context);,代码如下:

 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) {
         	...
          applicationManager =
              factory.build(
                  glide,
                  //绑定的生命周期是application
                  new ApplicationLifecycle(),
                  new EmptyRequestManagerTreeNode(),
                  context.getApplicationContext());
        }
      }
    }
    return applicationManager;
  }

    到此我们可以确认,如果你是在子线程调用Glide.with的话,那不管你是传入activity对象、Context对象,都是走了这个getApplicationManager方法拿到一个applicationManager,注意它是一个单例的RequestManager对象。
    它在构造RequestManager对象时传入的第二个参数是new ApplicationLifecycle(),其实这里不难看出,这是绑定了Application级别的声明周期。
    那如果在主线程,则get(activity)方法最后调用了supportFragmentGet方法:

  @NonNull
  private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
      //创建一个SupportRequestManagerFragment
    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
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      //绑定
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

    方法里首先出现了一个SupportRequestManagerFragment,点进去先不分析它的源码,只看他是继承了Fragment。
通过getSupportRequestManagerFragment方法来创建或者拿到SupportRequestManagerFragment。然后再创建一个RequestManager对象,注意这里创建RequestManager对象时传入的第二个参数是current.getGlideLifecycle(),也就是这个Fragment的生命周期。
到这里我们发现了,原来Glide在请求图片之前,它会创建一个SupportRequestManagerFragment对象,其实很多SDK都是这么做,通过创建一个空不可见的Fragment来同步Activity生命周期。
然后再对SupportRequestManagerFragment绑定一个ReuqestManager对象。
    至此我们先总结一下,使用Glide#with(activity)方法会在FragmentActivity里会创造一个SupportRequestManagerFragment,并且创建一个RequestManager。而且它们是1对1的,也就是一个Activity里只会有最多有一个SupportRequestManagerFragment对象和一个RequestManager对象,不会重复创建的。
而如果Glide#with(activity)方法是在子线程环境下调用的则绑定的是Application生命周期级别的一个RequestManager,它就跟Activity就没什么关系了。它跟Applcation也是1比1的。

    为什么如果是子线程就需要这么处理呢?其实涉及到Activity处理Fragment所以不能放在子线程,如果要Post到主线程的话又变成异步操作加大了获取难度。所以干脆就去拿applcation级别的RequestManager。只不过不同的是图片加载的生命周期就变成了application级别了,所以我们需要注意这一点,尽量不要在子线程去加载图片。

    那Glide#with方法除此之外还能接收View参数,其实本身也就是通过View获取Context对象,所以是一样的道理。
剩下最后一种是接收Fragment对象,这个也是同理,只不过如果传入的是Activity级别的,那么SupportRequestManagerFragment是附着在Activity里的,而如果传入的是Fragment对象,那么SupportRequestManagerFragment是作为child fragment附着到这个Fragment上的。
    我们容易看出他们最大的差别其实就是生命周期不同,因为生命周期的不同,后面加载过程的清除处理时机也就不同。
    所以到这里我们也建议了如果你是在Fragment里加载图片,那最好就不要使用Context,而是传入Fragment对象,这样图片加载的生命周期就会更短一些。

    所以噼里啪啦到这里才讲完了Glide#with的大致过程,其实我们根本都没讲Glide如何初始化的,如何读取AndroidMenfiest的和注解的GlideModule配置的。

RequestManager生命周期绑定

    上面我们知道了Glide是利用一个不可见的Fragment来实现生命周期绑定的,这里我们就来了解下具体是如何做绑定的。
我们来看看SupportRequestManagerFragment内部,在构造的时候:

  public SupportRequestManagerFragment() {
    this(new ActivityFragmentLifecycle());
  }
  @VisibleForTesting
  @SuppressLint("ValidFragment")
  public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
    this.lifecycle = lifecycle;
  }

可以看到创造了一个ActivityFragmentLifecycle对象,并且在几个周期方法里都转发给这个lifecycle:

  @Override
  public void onStart() {
    super.onStart();
    lifecycle.onStart();
  }

  @Override
  public void onStop() {
    super.onStop();
    lifecycle.onStop();
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    lifecycle.onDestroy();
    unregisterFragmentWithRoot();
  }

    可以看出SupportRequestManagerFragment其实自身做的生命周期处理就是分发,它本身并没有处理啥逻辑。重点自然而然就放在了ActivityFragmentLifecycle上,点进去看ActivityFragmentLifecycle代码,很欣慰,因为他只是一个Lifecycle的实现。内部维护了一个集合。
通过addListener方法来添加监听和removeListener移除,然后实现Lifecycle的方法就是做这个生命周期回调。
那不用想了,看下addListener方法谁在用,点出来就发现了RequestManager,还记得在构造RequestManager的时候传入的Lifecycle对象吗,是通过调用SupportRequestManagerFragment#getGlideLifecycle()方法传入的,然后又对这个Lifecycle对象调用addListener(this)把自己加入监听。
所以不难看出,这一层层的转发和回调,实际上就是为了让RequestManager具有生命周期的能力。

RequestManager内部加载图片过程

构造SingleRequest

上面我们获取到的RequestManager对象,如果我们是通过注解的方式,那么它其实是GlideRequests的实例,GlideRequests继承了RequestManager对象。原因是在上面我们看到获取这个对象都是用factory.build(),而这个factory实际就是GeneratedRequestManagerFactory
对象。在Glide初始化时initializeGlide方法里会通过GeneratedAppGlideModuleImpl这个实现类去创建这个factory。而这个factory在创建RequestManager实例的时候new出的是GlideRequests对象。初看GlideRequests也没啥差别,只是做了一层包装,利用后期拓展。而毕竟GlideRequests本身暂时也没做其它什么操作,所以主要还是在RequestManager对象内部。包括我们后续要讲的RequestBuilder对象,它实际也是GlideRequest的实例。
我们继续按着加载流程去走,经过Glide#with()之后我们通常接下来就是调用load方法了,那么我们直接看load方法好了。
load方法有太多个重载个了,它支持传入多钟类型的来源,因为我们通常也是用于加载网络图片的。在这里我们举一个重要的load(String string),它调用了父类RequestManager方法:

   public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }

先调用了一下asDrawable(),我们也很清楚,假如我们想得到Bitmap类型的,我们就可以在Glide.with(context).asBitmap()。
点进去asDrawable()就是调用了as方法并传入Drawable.class这个参数,通过as方法再创建了一个GlideRequest对象(GlideRequests重写了as方法,返回的是GlideRequest实例)记住这个Drawable.class参数最后是传给了GlideRequest里的transcodeClass变量。
接下来看GlideRequest的load方法,实现在RequestBuilder里:

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

这样就完了,这就是一个Builder模式,设置了model这个参数,注意model这个参数类型是Object,它就是各个load方法传入的参数。
然后其它的比如format方法、override方法、placeholder方法等我们暂时不管它,反正他们就是Builder构建,都是属于选项设置。
我们直接就来重头戏:into方法。into也有好多个重载方法,这里我们先看常用的into(ImageView)方法:

  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
  		...
      return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

源码上看,首先看到了熟悉的getScaleType(),看上去这是Glide在为每个不同的scaleType做不同的处理,我们先不去细挖它。
到最后又调用了into的重载方法,注意第一个参数是glideContext.buildImageViewTarget(view, transcodeClass)
transcodeClass记得么,就是我们上面传入的那个Drawable.class,点进去看buildImageViewTarget最终调用到了ImageViewTargetFactory#buildTarget方法:

  public <Z> ViewTarget<ImageView, Z> buildTarget(
      @NonNull ImageView view, @NonNull Class<Z> 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)");
    }
  }

这里就根据transcodeClass来创建对应的ViewTarget,比如我们上面用的是asDrawbale(),那这例创建的就是DrawableImageViewTarget实例了,我们继续看主流程,先不看DrawableImageViewTarget里的源码。
继续看接下来调用的另外一个into重载方法:

  private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,
      Executor callbackExecutor) {
    Preconditions.checkNotNull(target);
    //必须设置过来源
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
	//构造一个请求,后文再细说
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
    Request previous = target.getRequest();
    //如果当前的Reuqest和上一个request相等,并且满足情况那么直接重新再执行一遍,确保刷新。
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      // 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);
    //绑定request
    target.setRequest(request);
    //开始执行
    requestManager.track(target, request);

    return target;
  }

先判断isModelSet,这个变量就是当我们在调用load的时候就会设置为true的。然后通过buildRequest方法构建了一个我们还不清楚的Request对象
然后判断这个Target是不是已经绑定过Request了,并且满足情况的话就直接重新刷一遍。
具体满足什么情况呢,下面的源码注释解释了:如果请求已完成,则重新开始将确保重新;如果请求失败,将重新开始启动请求;如果请求已经在运行,我们可以让它继续运行而不中断。
有点拗口,反正这里也不重要,就是Glide做了一层确保。
然后源码往下来,先clear一遍,因为可能存在相同的target了,然后再 调用target.setRequest(request);把Reuqest绑定到这个target上。

  synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }
    /** Starts tracking the given request. */
  public void runRequest(@NonNull Request request) {
  	//记录该request
    requests.add(request);
    //如果不是暂停状态,那么就可以开始执行
    if (!isPaused) {
      request.begin();
    } else {
    //如果是暂停的,那么就先做清除处理,然后再把request加到pendingRequests里,等下次是非pause状态时再从pendingRequests取出来
      request.clear();
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request");
      }
      pendingRequests.add(request);
    }
  }

可以看出实际上requestManager.track(target, request);这句话就是分别对targetTracker变量和requests进行一次add,然后又一样调用request.begin()开启请求。这个targetTracker和requests我们姑且可以认为是两个容器,分别来存储target对象和request对象。
所以不难看出,接下来的操作就在request.begin()身上了。我们在开始begin之前得先了解这个request是什么来历。
在前面的into方法里,有这一句:

 Request request = buildRequest(target, targetListener, options, callbackExecutor);

我们追踪buildRequest方法,最终调用到buildRequestRecursive方法:

private Request buildRequestRecursive(
      Object requestLock,
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      BaseRequestOptions<?> requestOptions,
      Executor callbackExecutor) {
      ...
       ErrorRequestCoordinator errorRequestCoordinator = null;
      //如果通过调用error方法配置过,那么就不为空,构建的是一个ErrorRequestCoordinator类型的Request
    if (errorBuilder != null) {
      errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
      parentCoordinator = errorRequestCoordinator;
    }
      构造正常请求的Request
       Request mainRequest =
        buildThumbnailRequestRecursive(
            requestLock,
            target,
            targetListener,
            parentCoordinator,
            transitionOptions,
            priority,
            overrideWidth,
            overrideHeight,
            requestOptions,
            callbackExecutor);
	//如果没有定义error,那么正常就是使用SingleRequest
    if (errorRequestCoordinator == null) {
      return mainRequest;
    }
    ...
    //不然就是用这个ErrorRequestCoordinator
  errorRequestCoordinator.setRequests(mainRequest, errorRequest);
    return errorRequestCoordinator;
    }

这个调用链的源码实在太长了,我们全部贴出来就很难看,在buildRequestRecursive完成的工作就是判断需不需要构造error类型的请求(ErrorRequestCoordinator),
如果不需要那么就直接构造正常的Request(SingleRequest)。什么意思呢,就是在RequelstBuidler里面有个error方法,它支持我们当请求的url时产生错误则用另外另外一个备用的url来做代替。通常我们也比较少需要用到这个功能。所以这里我把buildRequestRecursive方法其它代码省略掉,逻辑会清晰一些,,而其中的buildThumbnailRequestRecursive方法,我们继续看:

  private Request buildThumbnailRequestRecursive(
      Object requestLock,
      Target<TranscodeType> target,
      RequestListener<TranscodeType> targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      BaseRequestOptions<?> requestOptions,
      Executor callbackExecutor) {
       if (thumbnailBuilder != null) {
       ...
       ThumbnailRequestCoordinator coordinator =
          new ThumbnailRequestCoordinator(requestLock, parentCoordinator);
		...
		 coordinator.setRequests(fullRequest, thumbRequest);
      return coordinator;
.
       }else if(thumbSizeMultiplier != null){
		...
		return coordinator;
}
else{
  return obtainRequest(
          requestLock,
          target,
          targetListener,
          requestOptions,
          parentCoordinator,
          transitionOptions,
          priority,
          overrideWidth,
          overrideHeight,
          callbackExecutor);
    }
}
}

    其实在buildThumbnailRequestRecursive里面又判断了我们有有没有配置缩略图。如果配置了缩略图那么就是ThumbnailRequestCoordinator实例,如果没有的话才是SingleRequest实例(obtainRequest方法里new出了一个SingleRequest)。

    所以这里我们总结一下这个Request的生成过程:它通过判断我们在Builder构造的时候有没有用error(@Nullable RequestBuilder errorBuilder),或者是有没有用到 thumbnail(
@Nullable RequestBuilder thumbnailRequest)

如果都没有,那么这个Request就是SingleRequest;
如果有构造Error时的处理,那么就是ErrorRequestCoordinator(它包裹了一个正常的Request和和error时请求的Request)
如果有构造thumbnail的处理,那么这个正常的Request就是ThumbnailRequestCoordinator类型(它包裹了一个正常的Request和一个缩略图的Request)。
    这里我们就摸索这个SingleRequest的情况好不好,不然太麻烦了。只要我们知道,这个调用链通过buildRequest()方法构造Request ->buildRequestRecursive()方法构造可能包含error处理的包裹Request - > buildThumbnailRequestRecursive()方法构造可能包含缩略图处理的包裹Request。

onSizeReady计算

    那接下来我们就可以进入SingleRequest#begin()方法来一探究竟了。begin方法代码不算特别多,全部贴上算了:

Override
public void begin() {
  synchronized (requestLock) {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    //检查来源是否为空,为空的话直接回调onLoadFailed方法
    if (model == null) {
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        width = overrideWidth;
        height = overrideHeight;
      }
      // Only log at more verbose log levels if the user has set a fallback drawable, because
      // fallback Drawables indicate the user expects null models occasionally.
      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 we're restarted after we're complete (usually via something like a notifyDataSetChanged
    // that starts an identical request into the same Target or View), we can simply use the
    // resource and size we retrieved the last time around and skip obtaining a new size, starting
    // a new load etc. This does mean that users who want to restart a load because they expect
    // that the view size has changed will need to explicitly clear the View or Target before
    // starting the new load.
    //源码也已经注释过了,如果说状态已经是加载完成了,那么直接就会回调onResourceReady方法了。
    if (status == Status.COMPLETE) {
      onResourceReady(resource, DataSource.MEMORY_CACHE);
      return;
    }

    // Restarts for requests that are neither complete nor running can be treated as new requests
    // and can run again from the beginning.
  //先假设status处于需要等待target测量宽高的状态
    status = Status.WAITING_FOR_SIZE;
    //如果宽高都是有效的,那么调用onSizeReady方法,在该方法里会把status置为Status.RUNNING状态
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      onSizeReady(overrideWidth, overrideHeight);
    } else {
    //如果还不确定宽高,那么就交给target自己去处理
      target.getSize(this);
    }
  //如果是进入运行状态了或者是等待布局大小的状态,那么就是正常的开始状态,会在这里回调target的onLoadStarted方法
    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));
    }
  }
}

    上面的注释已经写挺多了,在begin方法里主要做的工作就是除了做一下状态的校验,另外一个重点是规划图片的宽高,如果我们没调用过override方法去传入固定的宽高值,那么这里就会进入target.getSize(this);,那Glide是怎么确定宽高的呢,我们先摸清楚一下target.getSize(this)执行的流程,首先我们前面就讲到DrawableImageViewTarget的来源,也就是如果通过into(imageview)的话这里target要么就是DrawableImageViewTarget要么就是BitmapImageViewTarget,我们就先相当于是DrawableImageViewTarget,看getSize()方法是父类ViewTarget中的:

   void getSize(@NonNull SizeReadyCallback cb) {
      int currentWidth = getTargetWidth();
      int currentHeight = getTargetHeight();
      //如果宽高已经是有效了,那么就可以回调onSizeReady了
      if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
        cb.onSizeReady(currentWidth, currentHeight);
        return;
      }
	//否则下面就是对宽高获取的处理了
      // We want to notify callbacks in the order they were added and we only expect one or two
      // callbacks to be added a time, so a List is a reasonable choice.
      //把回调监听加入进来,等下获取到宽高后才可以回调出去
      if (!cbs.contains(cb)) {
        cbs.add(cb);
      }
      if (layoutListener == null) {
      //可以看到是利用ViewTreeObserver的形式,通过addOnPreDrawListener来监听View在draw阶段之前,如果view接下来要到draw阶段,那么它一定是已经知道宽高了。
        ViewTreeObserver observer = view.getViewTreeObserver();
        layoutListener = new SizeDeterminerLayoutListener(this);
        observer.addOnPreDrawListener(layoutListener);
      }
    }

//SizeDeterminerLayoutListener就是OnPreDrawListener,用来监听View在draw之前,把宽高回调出去
    private static final class SizeDeterminerLayoutListener
        implements ViewTreeObserver.OnPreDrawListener {
      private final WeakReference<SizeDeterminer> sizeDeterminerRef;

      SizeDeterminerLayoutListener(@NonNull SizeDeterminer sizeDeterminer) {
        sizeDeterminerRef = new WeakReference<>(sizeDeterminer);
      }

      @Override
      public boolean onPreDraw() {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
          Log.v(TAG, "OnGlobalLayoutListener called attachStateListener=" + this);
        }
        SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
        if (sizeDeterminer != null) {
        //只有这句代码有价值了 追踪这句代码
          sizeDeterminer.checkCurrentDimens();
        }
        return true;
      }
    }
    @Synthetic
    void checkCurrentDimens() {
      if (cbs.isEmpty()) {
        return;
      }
	//通过getTargetWidth()和getTargetHeight()来获取宽高
      int currentWidth = getTargetWidth();
      int currentHeight = getTargetHeight();
      //如果宽高还是无效的,那么就不会布局了
      if (!isViewStateAndSizeValid(currentWidth, currentHeight)) {
        return;
      }
	//通知回调
      notifyCbs(currentWidth, currentHeight);
      //事情已干完,对监听器做清除处理
      clearCallbacksAndListener();
    }
    //高度计算方式
       private int getTargetHeight() {
      int verticalPadding = view.getPaddingTop() + view.getPaddingBottom();
      LayoutParams layoutParams = view.getLayoutParams();
      int layoutParamSize = layoutParams != null ? layoutParams.height : PENDING_SIZE;
      return getTargetDimen(view.getHeight(), layoutParamSize, verticalPadding);
    }
 //宽度计算方式
    private int getTargetWidth() {
      int horizontalPadding = view.getPaddingLeft() + view.getPaddingRight();
      LayoutParams layoutParams = view.getLayoutParams();
      int layoutParamSize = layoutParams != null ? layoutParams.width : PENDING_SIZE;
      return getTargetDimen(view.getWidth(), layoutParamSize, horizontalPadding);
    }
      private void notifyCbs(int width, int height) {
      // One or more callbacks may trigger the removal of one or more additional callbacks, so we
      // need a copy of the list to avoid a concurrent modification exception. One place this
      // happens is when a full request completes from the in memory cache while its thumbnail is
      // still being loaded asynchronously. See #2237.
      for (SizeReadyCallback cb : new ArrayList<>(cbs)) {
        cb.onSizeReady(width, height);
      }
    }

    看上面这代码的流程,也就是当宽高已知,那么就回调onSizeReady,如果View还没有确定的宽高,那么就会利用ViewTreeObserver通过监听preDraw的形式来得知宽高,再回调onSizeReady()。
    至此,我们知道了begin()这个方法完成的最主要的工作还是对宽高的确定,确定了之后就通过onSizeReady()方法进入下一个环节。

开启DecodeJob加载任务

我们来看onSizeReady()方法:

  @Override
  public void onSizeReady(int width, int height) {
	...
	status = Status.RUNNING;
	//如果配置了大小倍数
	float sizeMultiplier = requestOptions.getSizeMultiplier();
      this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
      this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
	...
	      loadStatus =
          engine.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);
              ...
}

    可以看到onSizeReady方法里面主要就是engine.load()这个方法,这个看上去好像要进入加载阶段了。那这个engine是啥?,其实你有没有发现从开始构造Glide对象时到构造RequestManager、GlideRequest、SingleRequest这些实例时都传入了好多参数,都有传这个engine,
    追踪这个engine的来源才知道是在构造构造Glide对象时,也就是Glide在初始化时就做了这个操作,在前面我们没有细说Glide的初始化过程,这里用到了可以了解一下了。Glide的构造我们知道是在GlideBuilder的build方法中:

  Glide build(@NonNull Context context) {
  。。。
    if (engine == null) {
      engine =
          new Engine(
              memoryCache,//LruResourceCache 内存缓存器
              diskCacheFactory,//InternalCacheDiskCacheFactory 磁盘缓存器的创建工厂
              diskCacheExecutor,//磁盘缓存执行器 
              sourceExecutor,//资源加载器 线程池
              GlideExecutor.newUnlimitedSourceExecutor(), //资源加载器 无数量限制无核心数的线程池。 这两个线程池都是加载资源的,具体怎么使用通过外部配置
              animationExecutor,//动画执行器
              isActiveResourceRetentionAllowed);
    }
。。。

}

    在这里创建了一个加载引擎,就是Engine类的实例,并且把memoryCache、diskCacheExecutor、sourceExecutor等这些都创建一个实例传入进去。
    注意我们只讲Glide的主流程,并不会过多的去介绍和分析这些线程池啊、工厂策略等。
接下来我们就看重要的Engine类的load方法。

  public <R> LoadStatus load(
      GlideContext glideContext,
      Object model,
      Key signature,
      int width,
      int height,
      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;
	//关于缓存key的生成
    EngineKey key =
        keyFactory.buildKey(
            model,
            signature,
            width,
            height,
            transformations,
            resourceClass,
            transcodeClass,
            options);

    EngineResource<?> memoryResource;
    synchronized (this) {
    //先尝试读取一遍内存
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);

      if (memoryResource == null) {
      //读不到内存只能开启一个网络请求了
        return waitForExistingOrStartNewJob(
            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.
    //如果加载到了缓存,么就直接回调onResourceReady了
    cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
    return null;
  }

    load方法非常清晰,首先构造了个EngineKey,通过这个key尝试去获取缓存,获取不到就调用waitForExistingOrStartNewJob方法开启一个新任务,我们先简单看一下获取缓存的:

  @Nullable
  private EngineResource<?> loadFromMemory(
      EngineKey key, boolean isMemoryCacheable, long startTime) {
    if (!isMemoryCacheable) {
      return null;
    }

    EngineResource<?> active = loadFromActiveResources(key);
    if (active != null) {
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      return active;
    }

    EngineResource<?> cached = loadFromCache(key);
    if (cached != null) {
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return cached;
    }

    return null;
  }

    可以看到先调用了loadFromActiveResources()方法,有一个ActiveResources类记录了这些“活跃”的缓存,当这个加载器正在运行,name 它就是活跃的,当它进行了释放处理那么就是不活跃了。
    如果没命中这个活跃的缓存,那么就调用loadFromCache()方法,会去MemoryCache中去取了。MemoryCache就是在GlideBuilder中要构造Glide时创建的一个LruResourceCache。
    如果那得到缓存那么就不会再去加载了,这里我们先假设获取不到缓存,等我们捋清了网络请求过程这一块后再来说缓存(因为也只有经过请求后才可能有缓存吧)

看waitForExistingOrStartNewJob方法:

  private <R> LoadStatus waitForExistingOrStartNewJob(...){
		...
		 EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
		 //防止重复添加
		   if (current != null) {
		   ...
			return new LoadStatus(cb, current);
		}
		 //构建一个加载任务
		    EngineJob<R> engineJob = engineJobFactory.build(...);
		    //构建一个解码任务
		     DecodeJob<R> decodeJob =  decodeJobFactory.build(...);
		     //记录这个任务
		      jobs.put(key, engineJob);
		      ...
		      //开启任务
 			engineJob.start(decodeJob);
 			...
 			return new LoadStatus(cb, engineJob);
}

    这个方法语句不多,参数一堆,我省略了,只贴出重要的这几句代码。主要就是构造了一个EngineJob,通过EngineJobFactory构建出来的(内部维护了一个EngineJob的pool,防止频繁创建)
同时也创建了一个DecodeJob,通过DecodeJobFactory构建出来的(同理也是内部维护了一个DecodeJob的pool,重复利用对象)
那么任务就是由engineJob.start(decodeJob);这句代码开启了,点进去看start方法:

 public synchronized void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    GlideExecutor executor =
        decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
    executor.execute(decodeJob);
  }

    这里通过读取配置来决定是应该用哪个线程池,有diskCacheExecutor、sourceUnlimitedExecutor、animationExecutor、sourceExecutor
正常来说我们啥都没配置那就是sourceExecutor了。
然后执行到executor.execute(decodeJob); 就没了,发现是把decodeJob这个任务递交到线程池里执行。

尾声

    由于篇幅太长,在本篇我们就讲到了关于Glide.with(context)的过程,讲到了生命周期、讲到了加载任务。那么具体图片怎么去做请求,怎么解码的呢,我们看下篇:Glide4.x的源码解析(下).

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值