图片加载—Glide为什么这么强?Glide源码分析(上)

写在前面

Github–Glide 镇楼

源码越看觉得东西越多,决定分两篇来写:


代码实例

// how to use?
repositories {
  mavenCentral()
  google()
}

dependencies {
  implementation 'com.github.bumptech.glide:glide:4.11.0'
  annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}



// For a simple view:
@Override public void onCreate(Bundle savedInstanceState) {
  ...
  ImageView imageView = (ImageView) findViewById(R.id.my_image_view);

  Glide.with(this).load("http://goo.gl/gEgYUd").into(imageView);
}

// For a simple image list:
@Override public View getView(int position, View recycled, ViewGroup container) {
  final ImageView myImageView;
  if (recycled == null) {
    myImageView = (ImageView) inflater.inflate(R.layout.my_image_view, container, false);
  } else {
    myImageView = (ImageView) recycled;
  }

  String url = myUrls.get(position);

  Glide
    .with(myFragment)
    .load(url)
    .centerCrop()
    .placeholder(R.drawable.loading_spinner)
    .into(myImageView);

  return myImageView;
}

上面是官方给出的例子,下面是我写的例子hhh:

    // 梦开始的地方
    GlideApp.with(view.getContext())
         .load(url)
         .into(view);

Let’s begin!


GlideApp

Glide 4.x之前,未使用到GlideApp。4.x开始,Glide就不能调用placeHolder等函数了,而是需要通过GlideApp去调用才行。首先先看一下如何才能使用GlideApp:

GlideApp生成的方法:
①新建一个类继承AppGlideModule
②类添加GlideModule
③Make Module

import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;

@GlideModule
public final class MyAppGlideModule extends AppGlideModule {
      //可以配置Glide
}

为什么要使用GlideApp呢?Glide.with返回的是RequestManager对象,GlideApp.with返回的是GlideRequests,GlideRequests继承了RequestManager。

GlideRequests中的函数只是调用了super.xxx(),即RequestManager中的实现;在load(url)调用后,返回的GlideRequest对象,而原来返回的是RequestManager。我们配置的许多参数,例如placeHolder、error、asBitmap等,都是保存RequestOptions中的,而RequestManager中的RequestOptions并没有提供修改单一参数的方法。

下面我们来看一下GlideRequests的load:

  # GlideRequests
  
  @Override
  @NonNull
  @CheckResult
  public GlideRequest<Drawable> load(@Nullable String string) {
    // 调用RequestManager #load
    // GlideRequest<TranscodeType> 继承 RequestBuilder<TranscodeType>
    return (GlideRequest<Drawable>) super.load(string);
  }
  
  
  # RequestManager
  
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable String string) {
    // 1.保存TranscodeType
    // 2.设置加载数据的Model
    return asDrawable().load(string);
  }

这时链式调用里的对象就变成了GlideRequest,接下来我们来看一下placeHolder函数:

  # GlideRequest
  
  /**
   * @see GlideOptions#placeholder(Drawable)
   */
  @NonNull
  @CheckResult
  public GlideRequest<TranscodeType> placeholder(@Nullable Drawable drawable) {
    if (getMutableOptions() instanceof GlideOptions) {
      this.requestOptions = ((GlideOptions) getMutableOptions()).placeholder(drawable);
    } else {
      this.requestOptions = new GlideOptions().apply(this.requestOptions).placeholder(drawable);
    }
    return this;
  }

GlideRequest的placeHolder都是调用了GlideOptions的placeHolder。GlideOptions继承RequestOptions:

  # GlideOptions
  
  @Override
  @NonNull
  @CheckResult
  public final GlideOptions apply(@NonNull RequestOptions options) {
    return (GlideOptions) super.apply(options);
  }
  
  @Override
  @NonNull
  @CheckResult
  public final GlideOptions placeholder(@Nullable Drawable drawable) {
    return (GlideOptions) super.placeholder(drawable);
  }

总结一下:GlideApp返回的GlideRequests、GlideRequest实现了GlideOptions(RequestOptions)中单一参数的修改方法,链式调用更加方便,建议使用。


with(Context)

作为Glide的入口,先调用with函数传入Context,我们来看看with的代码:

  # GlideApp
  
  /**
   * @see Glide#with(Context)
   */
  @NonNull
  public static GlideRequests with(@NonNull Context context) {
    return (GlideRequests) Glide.with(context);
  }
  
  
  # Glide
  
  @NonNull
  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }
  
  @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);
  }
  
  @NonNull
  public static RequestManager with(@NonNull Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
  }
  
  @NonNull
  public static RequestManager with(@NonNull View view) {
    return getRetriever(view.getContext()).get(view);
  }

Glide提供了这些重载的方法方便了初始化操作。使用Context启动的任何请求将仅应用Application级别的选项,并且不会基于生命周期事件启动或停止。

在使用Glide加载图片资源时,图片资源的请求、加载应该跟随着Target的生命周期去走,即:如果ImageView在Activity中,我们应该在with中传入Activity对象;如果在Fragment中,那么我们应该传入Fragment对象,等等。这样,Glide才能正确的处理生命周期。

我们可以看到所有的重载都先调用了getRetriever:

  @NonNull
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    ··· // check not null
    return Glide.get(context).getRequestManagerRetriever();
  }
  
  // Glide.get(context)就是一个单例模式创建Glide对象,最后会调用到initializeGlide
  
  @SuppressWarnings("deprecation")
  private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    // get app context
    Context applicationContext = context.getApplicationContext();
    
    // 找到@GlideModule注解生成的实现类‘com.bumptech.glide.GeneratedAppGlideModuleImpl’
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
    
    List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
    if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
      manifestModules = new ManifestParser(applicationContext).parse();
    }

    ···

    // 将AppGlideModule中配置的各种参数赋值到GlideBuilder中
    // 1.RequestManagerFactory
    // 2.Options

    RequestManagerRetriever.RequestManagerFactory factory =
        annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory() : null;
    builder.setRequestManagerFactory(factory);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.applyOptions(applicationContext, builder);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
    
    // 生成Glide
    Glide glide = builder.build(applicationContext);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.registerComponents(applicationContext, glide, glide.registry);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
    }
    
    // 监听app生命周期
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
  }
  
  
  # RequestManagerRetriever
  
  // 根据Context类型调用不同的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)) {
      // 必须在UI线程
      
      if (context instanceof FragmentActivity) {
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        return get((Activity) context);
      } else if (context instanceof ContextWrapper) {
        return get(((ContextWrapper) context).getBaseContext());
      }
    }

    return getApplicationManager(context);
  }
  
  // 我们以FragmentActivity为例
  @NonNull
  public RequestManager get(@NonNull FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
      // 不在UI线程,改为AppContext
      return get(activity.getApplicationContext());
    } else {
      // activity不能被destroy
      assertNotDestroyed(activity);
      FragmentManager fm = activity.getSupportFragmentManager();
      return supportFragmentGet(
          activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }
  
  @NonNull
  private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
    
    // 注释1:获取SupportRequestManagerFragment,关于该类的分析请往下看
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
        
    // 为其设置RequestManager
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      
      // 创建RequestManager
      // 注释2:获取SupportRequestManagerFragment中的lifecycle,详情见下文
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
 
注释1

SupportRequestManagerFragment是干什么的呢?我们想象这样一个场景:你需要加载一张图片到activity,但是网络速度很慢。在图片加载完成之前,你关闭了activity,那么这时图片还应该继续加载到Target吗?显然是不应该。但是Glide本身无法感知组件的生命周期,所以就创建了一个无界面的fragment,即SupportRequestManagerFragment:

  @VisibleForTesting
  @SuppressLint("ValidFragment")
  public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
    this.lifecycle = lifecycle;
  }
  
  @Override
  public void onStart() {
    super.onStart();
    lifecycle.onStart();
  }

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

RequestManager的初始化传入了lifecycle:

  # RequestManager
  
  // RequestManager本身也实现了LifecycleListener
  RequestManager(
      Glide glide,
      Lifecycle lifecycle,
      ···) {
    this.glide = glide;
    this.lifecycle = lifecycle;
    ···

    // If we're the application level request manager, we may be created on a background thread.
    // In that case we cannot risk synchronously pausing or resuming requests, so we hack around the
    // issue by delaying adding ourselves as a lifecycle listener by posting to the main thread.
    // This should be entirely safe.
    if (Util.isOnBackgroundThread()) {
      mainHandler.post(addSelfToLifecycle);
    } else {
      lifecycle.addListener(this);
    }
    lifecycle.addListener(connectivityMonitor);

    ···
  }
 
  
  @Override
  public void onStart() {
    resumeRequests();
    targetTracker.onStart();
  }

  
  @Override
  public void onStop() {
    pauseRequests();
    targetTracker.onStop();
  }
  
  public void pauseRequests() {
    Util.assertMainThread();
    requestTracker.pauseRequests();
  }
  
  
  # RequestTracker
  
  public void pauseRequests() {
    isPaused = true;
    for (Request request : Util.getSnapshot(requests)) {
      if (request.isRunning()) {
        request.clear();
        pendingRequests.add(request);
      }
    }
  }

这样,当生命周期发生变化时,SupportRequestManagerFragment感知到变化并通知RequestManager,RequestManager通过RequestTracker来通知当前界面的Request,来做出对应的反应。


load

Glide能够加载的图片类型、来源也是多种多样,所有load也重载了很多方法。我们以最常见的String为例:

  # GlideRequests
  
  @Override
  @NonNull
  @CheckResult
  public GlideRequest<Drawable> load(@Nullable String string) {
    return (GlideRequest<Drawable>) super.load(string);
  }
  
  
  # RequestManager
  
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }
  
  
  // 1.asDrawable
  // 尝试始终使用任何可解码{@link Drawable}的任何子类的已注册{@link ResourceDecoder}加载资源。
  // 默认情况下,可以返回BitmapDrawable或GifDrawable,
  // 但如果为其他{@link Drawable}子类注册了其他解码器,则也可能会返回这些子类中的任何一个。
  // 2.load
  @NonNull
  @Override
  @CheckResult
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
  
  @NonNull
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    // 保存model
    this.model = model;
    
    // 调用into时会检查这个标志位
    isModelSet = true;
    return this;
  }

load调用完,链式中的对象就变成了GlideRequest,在这里简单介绍一下:

该类包含RequestBuilder中的所有public方法,RequestOptions中的所有options,以及GlideExtension注解的类中所有带注解的方法。它的方法主要就是调用RequestOptions的方法来为参数赋值,这里我们举一个例子来看一下:

GlideRequest

  /**
   * @see GlideOptions#diskCacheStrategy(DiskCacheStrategy)
   */
  @NonNull
  @CheckResult
  public GlideRequest<TranscodeType> diskCacheStrategy(@NonNull DiskCacheStrategy strategy) {
    if (getMutableOptions() instanceof GlideOptions) {
      this.requestOptions = ((GlideOptions) getMutableOptions()).diskCacheStrategy(strategy);
    } else {
      this.requestOptions = new GlideOptions().apply(this.requestOptions).diskCacheStrategy(strategy);
    }
    return this;
  }
  
  
  # GlideOptions
  
  @Override
  @NonNull
  @CheckResult
  public final GlideOptions diskCacheStrategy(@NonNull DiskCacheStrategy strategy) {
    return (GlideOptions) super.diskCacheStrategy(strategy);
  }
  
  
  # RequestOptions
  
  /**
   * Sets the {@link DiskCacheStrategy} to use for this load.
   *
   * <p> Defaults to {@link DiskCacheStrategy#AUTOMATIC}. </p>
   *
   * 大多数应用程序使用{@ link DiskCacheStrategy#RESOURCE}
   * 多次使用相同资源且具有多种大小,并希望在某些速度和磁盘空间之间进行权衡以换取较低带宽使用量的应用程序,
   * 可能需要考虑使用{@link DiskCacheStrategy#DATA}或{@link DiskCacheStrategy#ALL}。 </ p>
   */
  @NonNull
  @CheckResult
  public RequestOptions diskCacheStrategy(@NonNull DiskCacheStrategy strategy) {
    if (isAutoCloneEnabled) {
      return clone().diskCacheStrategy(strategy);
    }
    this.diskCacheStrategy = Preconditions.checkNotNull(strategy);
    
    // 标记该参数
    fields |= DISK_CACHE_STRATEGY;

    return selfOrThrowIfLocked();
  }

field也是一个经典用法,用于标记各个参数是否被主动赋值。各个参数的标记常量为:

field

即通过二进制位来标记所有参数。


into

首先我们来看一下into的流程:

  # RequestBuilder
  
  // 设置资源将被加载到的{@link ImageView}
  // 取消View中所有现有的加载,并释放Glide先前可能已加载到View中的任何资源,以便可以复用
  @NonNull
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    // UI线程
    Util.assertMainThread();
    // view不为null
    Preconditions.checkNotNull(view);

    RequestOptions requestOptions = this.requestOptions;
    
    // 如果设置了TransForm
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      
      // 不直接修改RequestOptions,而是clone出一个新的
      // 这样,复用时不会受到view的scaleType影响
      switch (view.getScaleType()) {
        case CENTER_CROP:
          // center crop
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          // center inside
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          // fit center
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
        case FIT_XY:
          // center inside
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }

    // 1.构建ViewTarget
    // 2.调用重载方法into
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions);
  }
  
  
  // 1
  # GlideContext
  
  @NonNull
  public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }
  
  
  # ImageViewTargetFactory
  
  @NonNull
  @SuppressWarnings("unchecked")
  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)");
    }
  }
  
  
  // 2
  # RequestBuilder
  private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @NonNull RequestOptions options) {
    // UI线程
    Util.assertMainThread();
    // target不为null
    Preconditions.checkNotNull(target);
    
    // 在load时讲过的,判断是否设置model的标记位
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }

    // build request
    options = options.autoClone();
    Request request = buildRequest(target, targetListener, options);

    // 与之前的request进行对比
    
    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      request.recycle();
      // 如果请求完成,则重新开始将确保结果重新传递,从而触发RequestListeners和Targets
      // 如果请求失败,则重新开始将重新启动请求,使它有另一个完成的机会
      // 如果请求已经在运行,我们可以让它继续运行而不会中断
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        // 使用上一个请求而不是新请求,达到优化的目的
        // 例如跳过设置占位符,跟踪和取消跟踪目标以及获取在单个请求中完成的视图维度
        previous.begin();
      }
      return target;
    }

    // 跟踪和取消跟踪目标,保存request
    requestManager.clear(target);
    target.setRequest(request);
    
    // 发起请求
    requestManager.track(target, request);

    return target;
  }
  
  
  # RequestManager
  
  void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }
  
  
  # RequestTracker
  
  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);
    }
  }

总结一下:先根据传入的View来创建ViewTarget,再根据options来创建Request,最后发起请求。而请求资源的过程就交给了RequestTracker,过程如何呢?又用了什么巧妙的方式来设计的呢?这篇已经太长了,我要分p了。


如果文章中有错误的地方,希望各位大佬们批评指正~

If you like this article, it is written by Johnny Deng.
If not, I don’t know who wrote it.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值