Glide 系列-2:主流程源码分析(4.8.0)

本文详细剖析了Glide在Android中的图片加载流程,从`with()`、`load()`到`into()`的每一步骤。首先,`with()`方法中实例化了Glide的单例,并进行了生命周期管理。`load()`方法用于构建请求,`into()`方法则按阶段开启DecodeJob,包括网络流的获取、输入流转换为Drawable以及显示到ImageView。整个过程涉及Glide的缓存策略、生命周期管理和请求管理,展示了Glide高效处理图片的机制。
摘要由CSDN通过智能技术生成

Glide 是 Android 端比较常用的图片加载框架,这里我们就不再介绍它的基础的使用方式。你可以通过查看其官方文档学习其基础使用。这里,我们给出一个 Glide 的最基本的使用示例,并以此来研究这个整个过程发生了什么:

Glide.with(fragment).load(myUrl).into(imageView);

上面的代码虽然简单,但是整个执行过程涉及许多类,其流程也比较复杂。为了更清楚地说明这整个过程,我们将 Glide 的图片加载按照调用的时间关系分成了下面几个部分:

  1. with() 方法的执行过程
  2. load() 方法的执行过程
  3. into() 方法的执行过程
    1. 阶段1:开启 DecodeJob 的过程
    2. 阶段2:打开网络流的过程
    3. 阶段3:将输入流转换为 Drawable 的过程
    4. 阶段4:将 Drawable 展示到 ImageView 的过程

即按照上面的示例代码,先分成 with()load()into() 三个过程,而 into() 过程又被细化成四个阶段。

下面我们就按照上面划分的过程来分别介绍一下各个过程中都做了哪些操作。

1、with() 方法的执行过程

1.1 实例化单例的 Glide 的过程

当调用了 Glide 的 with() 方法的时候会得到一个 RequestManager 实例。with() 有多个重载方法,我们可以使用 Activity 或者 Fragment 等来获取 Glide 实例。它们最终都会调用下面这个方法来完成最终的操作:

public static RequestManager with(Context context) {
   
    return getRetriever(context).get(context);
}

getRetriever() 方法内部我们会先使用 Glideget() 方法获取一个单例的 Glide 实例,然后从该 Glide 实例中得到一个 RequestManagerRetriever:

private static RequestManagerRetriever getRetriever(Context context) {
   
    return Glide.get(context).getRequestManagerRetriever();
}

这里调用了 Glide 的 get() 方法,它最终会调用 initializeGlide() 方法实例化一个单例Glide 实例。在之前的文中我们已经介绍了这个方法。它主要用来从注解和 Manifest 中获取 GlideModule,并根据各 GlideModule 中的方法对 Glide 进行自定义:

《Glide 系列-1:预热、Glide 的常用配置方式及其原理》

下面的方法中需要传入一个 GlideBuilder 实例。很明显这是一种构建者模式的应用,我们可以使用它的方法来实现对 Glide 的个性化配置:

private static void initializeGlide(Context context, GlideBuilder builder) {
   

    // ... 各种操作,略

    // 赋值给静态的单例实例
    Glide.glide = glide;
}

最终 Glide 实例由 GlideBuilderbuild() 方法构建完毕。它会直接调用 Glide 的构造方法来完成 Glide 的创建。在该构造方法中会将各种类型的图片资源及其对应的加载类的映射关系注册到 Glide 中,你可以阅读源码了解这部分内容。

1.2 Glide 的生命周期管理

with() 方法的执行过程还有一个重要的地方是 Glide 的生命周期管理。因为当我们正在进行图片加载的时候,Fragment 或者 Activity 的生命周期可能已经结束了,所以,我们需要对 Glide 的生命周期进行管理。

Glide 对这部分内容的处理也非常巧妙,它使用没有 UI 的 Fragment 来管理 Glide 的生命周期。这也是一种非常常用的生命周期管理方式,比如 RxPermission 等框架都使用了这种方式。你可以通过下面的示例来了解它的作用原理:

示例代码:使用 Fragment 管理 onActivityResult()

with() 方法中,当我们调用了 RequestManagerRetrieverget() 方法之后,会根据 Context 的类型调用 get() 的各个重载方法。

  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) {
   
        return get(((ContextWrapper) context).getBaseContext());
      }
    }

    return getApplicationManager(context);
  }

我们以 Activity 为例。如下面的方法所示,当当前位于后台线程的时候,会使用 Application 的 Context 获取 RequestManager,否则会使用无 UI 的 Fragment 进行管理:

  public RequestManager get(@NonNull Activity activity) {
   
    if (Util.isOnBackgroundThread()) {
   
      return get(activity.getApplicationContext());
    } else {
   
      assertNotDestroyed(activity);
      android.app.FragmentManager fm = activity.getFragmentManager();
      return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }

然后就调用到了 fragmentGet() 方法。这里我们从 RequestManagerFragment 中通过 getGlideLifecycle() 获取到了 Lifecycle 对象。Lifecycle 对象提供了一系列的、针对 Fragment 生命周期的方法。它们将会在 Fragment 的各个生命周期方法中被回调。

  private RequestManager fragmentGet(Context context, FragmentManager fm, 
    Fragment parentHint, boolean isParentVisible) {
   
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
   
      Glide glide = Glide.get(context);
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

然后,我们将该 Lifecycle 传入到 RequestManager 中,以 RequestManager 中的两个方法为例,RequestManager 会对 Lifecycle 进行监听,从而达到了对 Fragment 的生命周期进行监听的目的:

  public void onStart() {
   
    resumeRequests();
    targetTracker.onStart();
  }

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

1.3 小结

经过上述分析,我们可以使用下面的流程图总结 Glide 的 with() 方法的执行过程:

Glide 的 with() 方法的执行过程

2、load() 方法的执行过程

2.1 load() 的过程

当我们拿到了 RequestManager 之后就可以使用它来调用 load() 方法了。在我们的示例中传入的是一个 url 对象。load() 方法也是重载的,我们可以传入包括 Bitmap, Drawable, Uri 和 String 等在内的多种资源类型。示例中会调用下面的这个方法得到一个 RequestBuilder 对象,显然这是一种构建者模式的应用。我们可以使用 RequestBuilder 的其他方法来继续构建图片加载请求,你可以通过查看它的源码了解 Glide 都为我们提供了哪些构建方法:

  public RequestBuilder<TranscodeType> load(@Nullable String string) {
   
    return loadGeneric(string);
  }

RequestBuilder 的构造方法中存在一个 apply() 方法值得我们一提,其定义如下。从下面的方法定义中可以看出,我们可以通过为 RequestBuilder 指定 RequestOptions 来配置当前图片加载请求。比如,指定磁盘缓存的策略,指定占位图,指定图片加载出错时显示的图片等等。那么我们怎么得到 RequestOptions 呢?在 Glide 4.8.0 中的类 RequestOptions 为我们提供了一系列的静态方法,我们可以这些方法来得到 RequestOptions 的实例:

  public RequestBuilder<TranscodeType> apply(RequestOptions requestOptions) {
   
    Preconditions.checkNotNull(requestOptions);
    this.requestOptions = getMutableOptions().apply(requestOptions);
    return this;
  }

回过头来,我们可以继续跟踪 load() 方法。其实,不论我们使用了 load() 的哪个重载方法,最终都会调用到下面的方法。它的逻辑也比较简单,就是将我们的图片资源信息赋值给 RequestBuilder 的局部变量就完事了。至于图片如何被加载和显示,则在 into() 方法中进行处理。

  public RequestBuilder<TranscodeType> load(@Nullable String string) {
   
    return loadGeneric(string);
  }

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

2.2 小结

所以,我们可以总结 Glide 的 load() 方法的执行过程如下。也就是使用 RequestManger 得到一个 RequestBuilder 的过程:

Glide 的 load() 方法执行过程

3、into() 方法的执行过程

考虑到 into() 方法流程比较长、涉及的类比较多,我们按照图片加载的过程将其分成四个阶段来进行介绍。

第一个阶段是开启 DecodeJob 的过程。DecodeJob 负责从缓存或者从原始的数据源中加载图片资源,对图片进行变换和转码,是 Glide 图片加载过程的核心。DecodeJob 继承了 Runnable,实际进行图片加载的时候会将其放置到线程池当中执行。这个阶段我们重点介绍的是从 RequestBuilder 构建一个 DecodeJob 并开启 DecodeJob 任务的过程。即构建一个 DecodeJob 并将其丢到线程池里的过程。

第二个阶段是打开网络流的过程。这个阶段会根据我们的图片资源来从数据源中加载图片数据。以我们的示例为例,在默认情况下会从网络当中加载图片,并得到一个 InputStream.

第三个阶段是将输入流转换为 Drawable 的过程。得到了 InputStream 之后还要调用 BitmapFactorydecodeStream() 方法来从 InputStream 中得到一个 Drawable.

第四个阶段是将 Drawable 显示到 ImageView 上面的过程。

3.1 阶段1:开启 DecodeJob 的过程

3.1.1 流程分析

我们继续沿着 into() 方法进行分析。

into() 方法也定义在 RequestBuilder 中,并且也是重载的。不论我们调用哪个重载方法都会将要用来显示图片的对象封装成一个 Target 类型。Target 主要用来对用来显示图片的对象的生命周期进行管理。当我们要将图片加载到 ImageView 的时候,最终会调用下面的 buildTarget() 方法来讲我们的 ImageView 封装成一个 ViewTarget,然后调用 into() 的重载方法进行后续处理:

  public <Z> ViewTarget<ImageView, Z> buildTarget(ImageView view, 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值