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

Posted hymKing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最新源码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();
     else 
      //如果pause了,清理请求
      request.clear();
      if (Log.isLoggable(TAG, Log.VERBOSE)) 
        Log.v(TAG, "Paused, delaying request");
      
      //将其添加到等待队列
      pendingRequests.add(request);
    
  

上面的逻辑是先为 requests 添加一个请求,看看是否是停止状态,如果不是就调用request.begin(),核心调用逻辑应该就是是request.begin()方法。

查看调用发现,Request只是一个定义了请求相关抽象的接口。Request实现类有ErrorRequestCoordinator,ThumbnailRequestCoordinator,这两个实现类感觉是和Error、Thumbnail相关的,不应该是主流程,另一个实现类是SingleRequest ,上面有提到“Request对象的构建过程是怎样的?”,通过查看request对象的构建过程,我们最终能确认,最终构建返回的就是SingleRequest的一个实例。

看singleRequest.begin()方法的实现:

public final class SingleRequest<R> implements Request, SizeReadyCallback, ResourceCallback 
  @Override
  public void begin() 
    synchronized (requestLock) 
      ....
      if (status == Status.RUNNING) 
        throw new IllegalArgumentException("Cannot restart a running request");
      
      //表示自愿已经准备完成,直接回调回去
      if (status == Status.COMPLETE) 
        onResourceReady(
            resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
        return;
      
      //如果上面的逻辑没有被返回,则当成一个新的请求走下面的逻辑
      status = Status.WAITING_FOR_SIZE;
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) 
        //size校验,合法大小,开始加载
        onSizeReady(overrideWidth, overrideHeight);
       else 
        //size为校验好,就获取size
        target.getSize(this);
      

      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
          && canNotifyStatusChanged()) 
        //给目标对象回调图片加载开始,可以理解成图片起点的回调
        target.onLoadStarted(getPlaceholderDrawable());
      
      .....
    
  

2.3.2 执行引擎Engine的调度

上述源码中,在大小校验合法的情况下,开始加载的流程是回调了onSizeReady()方法。

@Override
public void onSizeReady(int width, int height) 
  stateVerifier.throwIfRecycled();
  synchronized (requestLock) 
    ...
    status = Status.RUNNING;
    ...
    loadStatus =
        //这里将请求任务交给engine
        engine.load(
            glideContext,
            model,
            requestOptions.getSignature(),
            this.width,
            this.height,
            requestOptions.getResourceClass(),
            transcodeClass,
            priority,
            //各种请求选项,这里没搞明白为啥不直接把requestOptions作为参数呢
            requestOptions.getDiskCacheStrategy(),
            requestOptions.getTransformations(),
            requestOptions.isTransformationRequired(),
            requestOptions.isScaleOnlyOrNoTransform(),
            requestOptions.getOptions(),
            requestOptions.isMemoryCacheable(),
            requestOptions.getUseUnlimitedSourceGeneratorsPool(),
            requestOptions.getUseAnimationPool(),
            requestOptions.getOnlyRetrieveFromCache(),
            this,
            callbackExecutor);

   ....
  

接下来调用的是Engine.load()方法:

/** 响应开始加载的请求和管理活跃和缓存的资源 */
public class Engine
    implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener 
      //引擎中load方法的实现
      public <R> LoadStatus load(...一堆参数(说实话没明白为啥要定义这么多参数的方法)
      ) 
    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
    //使用key工厂构建一个key
    EngineKey key =
        keyFactory.buildKey(
            ....);
    //声明一个内存资源对象
    EngineResource<?> memoryResource;
    synchronized (this) 
      //从内存中获取资源,内部实现了获取活动资源和cache资源,都属于内存缓存
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
      //从内存缓存中未获取到资源
      if (memoryResource == null) 
        //等待一个正在执行的符合条件的任务or启动一个新任务
        return waitForExistingOrStartNewJob(
            ....一堆参数);
      
    
    //获取到了资源直接回调出去
    cb.onResourceReady(
        memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
    return null;
    
  /**等待一个正在执行的符合条件的任务or启动一个新任务*/
  private <R> LoadStatus waitForExistingOrStartNewJob(
      ....一堆参数....) 
    //获得是否有符合条件的正在执行的任务(这里的条件判断的就是通过key来判断)
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    //获得到
    if (current != null) 
      //添加回调并返回
      current.addCallback(cb, callbackExecutor);
      return new LoadStatus(cb, current);
    
    //执行到这,说明没有可用的缓存,也没有正在执行的job和当前的请求是一致的,于是创建新的工作引擎
    EngineJob<R> engineJob =
        //通过引擎工厂构建
        engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            on

以上是关于最新源码Glide4.12框架之加载图片流程源码分析的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

Glide 4.12 框架源码中的生命周期设计

Glide 4.12 框架源码中的生命周期设计

Glide 4.12 框架源码中的生命周期设计