最新源码Glide4.12框架之加载图片流程源码分析

一、前言

Android图片加载框架,在android应用开发中是一个常见的话题。在12、13年的时候我记得可能用的最多的是XUtils的一套框架(更早之前叫aFinal框架),这个框架中提供imageUtils用于在android应用的开发中完成远程图片的加载。再后来呢,有Picasso、Fresco、Glide。而这几年的开发经验来看,Glide最为流行。不信,可以查看github上项目地址,分别对比对比watch数、fork数、star数,就能确定Glide确实最为流行。

如何对图片框架技术进行选型?

通过这种对比watch、fork、star数的方式,可以作为我们开发一款非特定需求应用的技术选型方式,毕竟对比清楚每一个框架之间的差异,是一件比较耗时、也是不容易的事情。除了图片加载框架,比如我们平常在为了解决项目中某些其它通用轮子问题的时候,我们可能逛街github,挑选免费的开源源码商品,这个时候,watch、fork、star数也可以作为我们选定某个框架的依据。一般即使是小众一点轮子开源源码star数也至少大于1000,我们才能考虑选型,star数代表阅读过此项目的同学的认可。另外一点,就是看issues的活跃度情况,比如有多少个issues,回复的及时性等情况如何。

上面一部分,介绍Android端图片加载框架的简单历程,插入了对于技术选型的非技术角度的手段的选型技巧概述。接下来我们还是开始介绍一下最流行的Glide图片加载框架。

Glide图片加载框架的优势有哪些?

Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。

Glide 支持拉取,解码和展示视频快照,图片,和GIF动画。Glide的Api是如此的灵活,开发者甚至可以插入和替换成自己喜爱的任何网络栈。默认情况下,Glide使用的是一个定制化的基于HttpUrlConnection的栈,但同时也提供了与Google Volley和Square OkHttp快速集成的工具库。

虽然Glide 的主要目标是让任何形式的图片列表的滚动尽可能地变得更快、更平滑,但实际上,Glide几乎能满足你对远程图片的拉取/缩放/显示的一切需求。

以上是官方文档对Glide的介绍内容,末尾四个字硬气的很,“一切需求”。因为这篇是源码分析篇,不会过多介绍Glide图片加载框架的使用。

本篇文章的下文主要分为两个部分:(文章内容太长,回过头来补充说明下)

**第一部分:**按照图片的加载流程通读源码实现,并进行注释解释说明。(这部分内容比较枯燥,读者可以自己追踪源码走一遍流程。)

**第二部分:**对整体的图片加载流程进行概括,梳理核心流程,并绘制加载流程图。

另外,除了本篇的加载流程分析之外,会计划Glide源码在设计层面的一些解析和分析,包括Glide生命周期的设计分析、Glide缓存的设计分析。

二、Glide框架图片加载流程原理分析

源码版本为:4.12.0,为当前最新源码版本。

接下来我以一个最简单的Demo调用作为源码分析的入口,开始源码分析。

public class MainActivity extends AppCompatActivity {
   
    String url = "";
    ImageView imageView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
   
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        imageView = findViewById(R.id.img_glide);
        //通过链式调用的方式加载一张图片
        Glide.with(this).load(url).into(imageView);
    }
    //with、load、into三个方法分解后的调用
    void glideLoadImg() {
   
        RequestManager requestManager = Glide.with(this);
        RequestBuilder requestBuilder=requestManager.load(url);
        requestBuilder.into(imageView);
    }
}
2.1 with方法获得RequestManger的对象

上述代码链式调用的代码,分解成了glideLoadImg()方法中的三行代码的调用,首先我们看Glide.with(this)方法的源码。

/**
* Glide是一个单例,简单的静态实现。作用是为RequestBuilder构建Requests和维护Engine、
* BitmapPool以及DiskCache和MemoryCache
*/
public class Glide implements ComponentCallbacks2 {
   

  /**
   * 获取的requestManager,在load之前需要调用,和Activity的lifecycle进行关联
   */
  @NonNull
  public static RequestManager with(@NonNull Activity activity) {
   
    return getRetriever(activity).get(activity);
  }
}

这个with方法的调用,最终返回的是一个RequestManager对象实例,RequestManager对象的获取是通过RequestManagerRetriever.get()方法获取。RequestManagerRetriever对象的职责就是用来创建和获取RequestMangaer对象的,在RequestManager内部通过RequestManagerFactor工厂进行最终的创建。

/**
 * 创建一个新的requestManager或者从现有的Activity、fragment中检索一个已经存在ReqeustManager
 * 
 */
public class RequestManagerRetriever implements Handler.Callback {
   
private RequestManager fragmentGet(...) {
   
    ...
    //获取已经存在的RequestManager
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
    RequestManager requestManager = current.getRequestManager();
    //没有获取到,通过工厂创建
    if (requestManager == null) {
   
      //获取单例的Glide对象
      Glide glide = Glide.get(context);
      //通过工厂创建requestManager
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      if (isParentVisible) {
   
        requestManager.onStart();
      }
      //绑定
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
}
/**
 *
 * @see Glide#with(android.app.Activity)
 * @see Glide#with(androidx.fragment.app.FragmentActivity)
 * @see Glide#with(android.app.Fragment)
 * @see Glide#with(androidx.fragment.app.Fragment)
 * @see Glide#with(Context)
 */
public class RequestManager{
   
   @Override
  public synchronized void onStart() {
   
  }
   */
  @Override
  public synchronized void onStop() {
   
    
  }
  @Override
  public synchronized void onDestroy() {
   
    
  }
}

RequestManager有上述5种重载方法可以得到,最终都会调用到fragmentGet()方法,整体流程是一致的。

RequestManager是一个Glide框架管理和开始请求的一个类。可使用Activity、Fragment以及关联其生命周期事件去智能的停止、开始、重新开始请求。可以理解成Glide的生命周期的管理类,在上述简化版的源码中,我们也看到了类似Activity生命周期的回调方法 onStart()、onStop()、onDestoy()。

2.2 load方法获取RequestBuilder对象

requestBuilder对象的获取是通过RequestManager来完成的,看如下源码实现

public class RequestManager ...{
    
  /**
   * 这里面也是链式调用,相当于先调用了asDrawable()方法,然后调用requestBuilder.load(String)
   * 返回一个新的RequestBuilder去加载使用指定的model去加载drawable
   */
  @Override
  public RequestBuilder<Drawable> load(@Nullable String string) {
   
    return asDrawable().load(string);
  }
}

上述asDrawable()、load()方法的调用都是返回一个RequestBuilder对象,最终链式调用的结果也是返回一个RequestBuilder的对象。load()方法的实现在RequestBuilder类源文件中。

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
   
  //返回的是一个加载给定字符串的requestBuilder对象
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
   
    return loadGeneric(string);
  }
  @NonNull
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
   
    if (isAutoCloneEnabled()) {
   
      return clone().loadGeneric(model);
    }
    this.model = model;
    isModelSet = true;
    return selfOrThrowIfLocked();
  }
}

总之,从上述源码中可以看到,通过两次链式调用后,返回ReqeustBuilder实例,内部调用loadGeneric()对一些全局属性进行了设置,最终以RequestBuilder对象返回。

RequestBuilder:一个通用类,可以处理通用资源类型的设置选项和启动加载。说白了,为后续环节真正的发起图片加载做准备工作的。

2.3 into方法的核心实现

into方法,是我们通过Glide加载图片调用的最后一个方法,with方法、load方法可以理解成都是为into方法的核心逻辑做准备的,into方法中会涉及到发起远程图片加载的核心逻辑。看RequestBuilder.into()方法的源码实现:

/**
 * into方法接受的参数是imageView,imageView就是资源(url\uri...)要被载入的目标
 * 取消任何现有的加载到视图中,并释放 Glide 之前可能加载到视图中的任何资源,以便它们可以被重用。
 * Return:方法返回的ViewTarget是传入参数imageView的包装
 */
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
   
  ....
  BaseRequestOptions<?> requestOptions = this;
  if (!requestOptions.isTransformationSet()
      && requestOptions.isTransformationAllowed()
      && view.getScaleType() != null) {
   
    // View ScaleType的设置
    switch (view.getScaleType()) {
   
      case CENTER_CROP:
        requestOptions = requestOptions.clone().optionalCenterCrop();
        break;
      ....
      default:
        // Do nothing.
    }
  }
  //会继续调用下面的重载的into方法
  return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        //主线程池
        Executors.mainThreadExecutor());
}

 private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,
      Executor callbackExecutor) {
   
    Preconditions.checkNotNull(target);
    /**这个isModelSet是在load方法中设置为True的,如果未调用load方法直接调用into方法抛出异常
     *我们自己在设计一些类中的方法时,如果需要做校验,也可以通过这种抛出异常的方法,来提醒调用者
     *使用正确的方式调用*/
    if (!isModelSet) {
   
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
    //构建Request
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
    //获取当前目标对象上先前是否已存在的Request
    Request previous = target.getRequest();
    //如果这两个request相同&&不跳过内存缓存
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)
       /**这个方法的细节,可以查看源码注释*/) {
   
      if (!Preconditions.checkNotNull(previous).isRunning()) {
   
        previous.begin();
      }
      //目标request可复用,直接返回
      return target;
    }
    requestManager.clear(target);
    //给目标设置新创建的Request
    target.setRequest(request);
    //通过RequestManager调用track方法
    requestManager.track(target, request);

    return target;
  }

当我们调用RequestBuilder.into方法时,RuquestBuilder对象帮我们构造或是重用Request实例对象。最终将request实例通过requestManger.track()方法传入。接下来就重新回到RequestManger方法中来。

public class RequestManager...{
   
	synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
   
    targetTracker.track(target);
    //看到这个方法,是不是感觉真实的请求调用就在此了
    requestTracker.runRequest(request);
  }
}

这里面仍然有一个问题,我们没有去分析,就是在上一段源码中,Request对象的构建过程是怎样的?

2.3.1 requestTracker请求调用的开始

我们先先顺着看requestTracker.runRequest(request)的方法的实现:

public class RequestTracker {
   
  //请求队列
  private final Set<Request> requests =
      Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
  //等待队列
  private final List<Request> pendingRequests = new ArrayList<>();
  
  /** 开始追踪一个指定的请求 */
  public void runRequest(@NonNull Request request) {
   
    //将request添加到请求队列
    requests.add(request);
    if (!isPaused) {
   
      //没有被pause,开始发起请求
      request.begin(<
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hymKing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值