知识梳理系列之七——Glide的原理

知识梳理系列之七——Glide的原理

  • Glide的基本使用
  • Glide源码解析
    • Glide的三步曲分别做了什么?
      • 1. 第一步Glide.with(Context);
        • 1.1 初探with
        • 1.2 getRetriever获取请求管理索引器对象
        • 1.3 RequestManagerRetriever.get()获取Glide.with()的返回值
        • 1.4 总结:
      • 2. 第二步RequestManager.load
        • 2.1 创建RequestBuilder
        • 2.2 load
      • 3. 第三步into
        • 3.1 看来重点在这了,毕竟load没干啥
        • 3.2 真正的请求
    • Glide如何缓存图片?
    • Glide最终是怎样加载资源的?
    • Glide如何处理生命周期?
  • Glide与Picaso、Fresco的对比

Glide的基本使用

常用的使用方式如下,特点是链式调用,with/load/into是必不可少的,还可以添加图片数据格式、占位图、错误占位图、缓存机制、网络请求等等配置。

Glide.with(Context)
	.load(StringUrl)
	.into(ImageView);

总而言之,功能十分强大而不需要自己实现图片的加载、数据的转换、缓存等等。
那么他是怎样帮我们把这些都处理完的呢,看下面解析。


Glide源码解析

Glide的三步曲分别做了什么?

1. 第一步Glide.with(Context);

with方法有多个,都是传入Context(Context的子类如:Activity、Fragment,支持support和app包下的两种Fragment,支持FragmentActivity) 或者 View(最终还是通过view获取了Context);

1.1 初探with

源码大同小异,基本都是调用了{@link Glide#getRetriever(Context)}和{@link RequestManagerRetriever#get()}两个方法;
特别注意,这里的返回值是RequestManager对象,后面会用到!!

// Glide.java
  @NonNull
  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }

1.2 getRetriever获取请求管理索引器对象

// Glide.java 
  @NonNull
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    //... 检查Context不能为空
    return Glide.get(context).getRequestManagerRetriever();
  }

  // 1. volatile 关键字已经在多线程中分析过,用来保证可见性和部分有序性;
  private static volatile Glide glide;
  // 2. 可以看出Glide的实例是单例的;
  @NonNull
  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      // 3. 此方法通过反射创建了一个GeneratedAppGlideModuleImpl的实例
      GeneratedAppGlideModule annotationGeneratedModule =
          getAnnotationGeneratedGlideModules(context.getApplicationContext());
      synchronized (Glide.class) {
        if (glide == null) {
          // 4. 真正的检查和初始化Glide实例;
          checkAndInitializeGlide(context, annotationGeneratedModule);
        }
      }
    }
    return glide;
  }

在上述的源码可以看出:
a. 在获取请求管理索引器对象的过程中,先创建了Glide的实例,并对其进行初始化,Glide是单例的,使用了双重校验锁单例模式
b. 使用反射,创建了GeneratedAppGlideModuleImpl实例


继续看源码:(这部分可以简略看下或直接看总结

// Glide.java
  private static void checkAndInitializeGlide(
    // ... 使用布尔标志判断是否已经初始化
    initializeGlide(context, generatedAppGlideModule);
    // ...
  }
  // 1. 终于到核心方法执行初始化操作了, 这期间通过同名方法new了一个GlideBuilder实例
  private static void initializeGlide(
      @NonNull Context context,
      @NonNull GlideBuilder builder,
      @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
    ...
    List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
    ...
      // 2. 从清单配置文件中解析出GlideModule列表
      manifestModules = new ManifestParser(applicationContext).parse();
    ...
    // 3. 根据是否配置了GlideModule来决定是否需要使用其统一配置的Options,以及统一注册的Componnets
    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);
    }
    // 4. 最重要的一步!!!
    // 使用GlideBuilder构造器类构造出了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);
    }
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
  }

// GlideBuilder.java
  // 简化了所有判空和部分传参
  // 可以看出build为Glide的创建,初始化了大量引用对象!!!
  Glide build(@NonNull Context context) {
      sourceExecutor = GlideExecutor.newSourceExecutor();
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
      animationExecutor = GlideExecutor.newAnimationExecutor();
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
      connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
        bitmapPool = new LruBitmapPool(size);
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
      engine = new Engine(...);
    RequestManagerRetriever requestManagerRetriever =
        new RequestManagerRetriever(requestManagerFactory);
    return new Glide(...);
  }

在上述的源码可以看出:
a. 检查和初始化方法检查了是否有GlideModule的实现类:
这个类是给Glide做统一配置的。当业务需要多处加载图片使用Glide,为方便配置可自行实现一个GlideModule,并且在AndroidManifest.xml文件中的节点下,配置节点指向自定义的GlideModule类,进而实现统一配置。
继承GlideModule,需要实现applyOptions、registerComponent方法,分别的作用是:做统一配置(如LruCache的策略、DiskLruCache的策略、编码格式、Bitmap池等等);向Glide注册自定义组件(这种可以替换默认的网络请求套件、数据拦截器等)。

b. 实例化了Glide单例:
使用GlideBuilder构造出Glide实例,并在build方法中配置了资源线程池、缓存处理线程池、内存计算器、Bitmap池、连接监控工厂、缓存、加载引擎、请求管理索引器等。

现在回到1.2开头时分析的get方法,这时候的getRequestManagerRetriever方法就是获取的Glide对象中的RequestManagerRetriever对象的引用。


1.3 RequestManagerRetriever.get()获取Glide.with()的返回值

上一步构造Glide并获取Glide中创建的RequestManagerRetriever实例后,继续调用RequestManagerRetriever的get,最后调用下面的方法:

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

这个方法很简单:
就是获取RequestManager对象或者用工厂构造它,其中传入了current.getGlideLifecycle,看名字大胆猜测是用于Activity或者Fragment的生命周期管理的!事实也正是这样。

1.4 总结:

1. with方法解析了清单配置文件中的统一选项配置、组件注册类GlideModule,并把统一配置放入了GlideBuilder,注册组件放入了Glide的Register;
2. with方法用GlideBuilder构造了Glide的单例,初始化了缓存、请求管理、线程池、资源池、加载引擎等重要对象;
3. with方法最终返回了管理请求、生命周期的RequestManager实例。


2. 第二步RequestManager.load

2.1 创建RequestBuilder

load方法分成了两步,一是as,一是load。as方法就是创建了RequestBuilder对象。

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

2.2 load

发现load方法几乎没干啥,就是给model赋值,比如传入一个字符串型的url,就吧这个字符串赋给了model。然后离了个flag表明已经model赋值了。

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

3. 第三步into

3.1 看来重点在这了,毕竟load没干啥

// RequestBuilder.java
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    ...
    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) {
    ...
    // 1. 核心方法:buildRequest 
    Request request = buildRequest(target, targetListener, options, callbackExecutor);

	// 2. 对比相同的ImageView之前的请求有啥不同的
    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        previous.begin();
      }
      return target;
    }
    // 3. 不同的请求才进入这里,整整设置了请求并开始追踪任务
    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);

    return target;
  }

上述代码:
a. into最终构造了请求,通过buildRequest方法,构造了Request对象,并且根据情况构造了缩略图Request、错误Request对象;
b. 避免了相同请求再次被追踪,不同请求则被执行了。

3.2 真正的请求

找到执行请求的语句:

  synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }

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

很容易看出:runRequest就是关键锁在了!!!
Request是一个接口,所以begin等方法需要到SingleRequest实现类。
知识梳理系列之七——Glide的原理_第1张图片
基本流程就分析到这,再深入继续往下:


Glide如何缓存图片?

真正的执行请求方法:SingleRequest.begin:

// SingleRequest.java
  @Override
  public void begin() {
    synchronized (requestLock) {
      ...
      if (model == null) {
	    // ... 之前赋值过model的,如果是空抛异常
	  }
      ... 
	  status = Status.WAITING_FOR_SIZE;
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        // 等待尺寸准备好
       	onSizeReady(overrideWidth, overrideHeight);
      } else {
        target.getSize(this);
      }
      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
          && canNotifyStatusChanged()) {
        target.onLoadStarted(getPlaceholderDrawable());
      }
	  ...
    }
  }

  @Override
  public void onSizeReady(int width, int height) {
  	...
  	loadStatus = engine.load(.../**此处省略N个参数*/);
  	...
  }

生活终于对小猫咪下手了:
RequestBuilder.into --> RequestManager.track --> Request.begin --> SingleRequest.begin --> SingleRequest.onSizeReady --> Engine.load

  public <R> LoadStatus load(...) {
  	// 1. 根据图片的url、签名、尺寸、选项等生成一个key
  	EngineKey key =
        keyFactory.buildKey(
            model,
            signature,
            width,
            height,
            transformations,
            resourceClass,
            transcodeClass,
            options);
    EngineResource<?> memoryResource;
    synchronized (this) {
      // 2. 从内存中加载图片资源
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
	  // 3. 内存没有,从磁盘加载或者网络获取
      if (memoryResource == null) {
        return waitForExistingOrStartNewJob(...);
      }
      ...
    }
  }

从load方法中可以看到两个重要的步骤:

  1. 先根据key来获取缓存中的图片数据;loadFromMemory方法
  2. 如果缓存中资源未空,那么执行准备推出或者启动新任务方法,执行新任务就是从磁盘或者网络获取图片数据。waitForExistingOrStartNewJob方法

继续深入loadFromMemory和waitForExistingOrStartNewJob:

  private EngineResource<?> loadFromMemory(EngineKey key, boolean isMemoryCacheable, long startTime) {
    // 1. 如果设置的缓存策略是不缓存,那么直接返回空
    if (!isMemoryCacheable) {
      return null;
    }

    EngineResource<?> active = loadFromActiveResources(key);
    if (active != null) {
      ...
      // 2. 从loadFromActiveResources资源引用队列中获取图片,非空则返回
      return active;
    }

    EngineResource<?> cached = loadFromCache(key);
    if (cached != null) {
      ...
      // 3. 从loadFromCache内存缓存中获取图片,非空则返回
      return cached;
    }

    return null;
  }

这里可以很清晰的看出Glide图片缓存的其中两级:一级:引用队列缓存;二级:内存缓存
来看下具体是怎么缓存的以及缓存所用的数据结构:
loadFromActiveResources方法调用了ActiveResources的get方法

// ActiveResources.java  
  // 1. 定义了一个HashMap的成员变量用来存储图片资源的弱引用;
  final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
  // 2. 其中HashMap的Value数据结构是一个弱引用
  static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {...}
  // 3. 使用HashMap的get方法获取图片弱引用
  synchronized EngineResource<?> get(Key key) {
    // 从HashMap中获取资源弱引用
    ResourceWeakReference activeRef = activeEngineResources.get(key);
    // 弱引用为空,返回空
    if (activeRef == null) {
      return null;
    }
    // 从弱引用中获取资源强引用
    EngineResource<?> active = activeRef.get();
    // 资源强引用为空,清除key并返回空;资源强引用不为空,返回资源强引用
    if (active == null) {
      cleanupActiveReference(activeRef);
    }
    return active;
  }

一级缓存: 一个存储活跃图片资源弱引用的HashMap!
1. 将active的图片的弱引用存储在一个HashMap中(Value是一个EngineResource的弱引用);
2.通过WeakReference.get方法获取图片资源。

下面看二级缓存:

loadFromCache方法

//  Engine.java
  private EngineResource<?> loadFromCache(Key key) {
    // 1. 从缓存中获取,实际是调用cache的remove方法返回EngineResource
    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
      cached.acquire();
      // 2. 然后将EngineResource重新put到队列中(回到了一级缓存中);
      activeResources.activate(key, cached);
    }
    return cached;
  }

  // 3. MemoryCache是一个接口,其实现类是LruResourceCache
  private final MemoryCache cache;
  private EngineResource<?> getEngineResourceFromCache(Key key) {
    // 4. 从LRU缓存中移除Key对应的资源并返回被移除的资源;
    Resource<?> cached = cache.remove(key);

    //... 进行非空判断和类型转换
    return cached;
  }

二级缓存:一个LruCache!
1. 使用LruCache缓存数据,当从二级缓存中找到资源,则从二级缓存移除并返回该资源;
2. 二级缓存中被移除的资源最后被放入了一级缓存HashMap,并以弱引用存在。

下面看三级缓存:

// Engine.java
  private <R> LoadStatus waitForExistingOrStartNewJob(...) {// 省略参数列表
    // 1. 从HashMap中查找是否曾经执行过相同的任务
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      // 不为空说明原来执行过加载任务,只需要从磁盘中重新加载即可
      current.addCallback(cb, callbackExecutor);
      ...
      return new LoadStatus(cb, current);
    }

    // 2. 未执行过此图片加载任务,创建任务并放入HashMap保存,然后使用引擎启动任务
    EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache);

    DecodeJob<R> decodeJob = decodeJobFactory.build(glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired,  isScaleOnlyOrNoTransform, onlyRetrieveFromCache, options, engineJob);

    jobs.put(key, engineJob);

    engineJob.addCallback(cb, callbackExecutor);
    engineJob.start(decodeJob);
    ...
    return new LoadStatus(cb, engineJob);
  }

三级缓存:返回一个LoadStatus对象
1. 先判断是否执行过加载任务,如果执行过,说明在磁盘中有缓存,去磁盘decode图片资源;
2. 若没有加载过,则创建新的加载任务,并放入任务HashMap,然后执行网络加载。


Glide最终是怎样加载资源的?

上一节进入了Engine.load根据不同的情况:

  1. 弱引用HashMap中有,直接通过key来获取资源并返回;
  2. LruCache中有,则通过key获取cache中的资源,并将资源从LruCache中删除,并放入弱引用HashMap,返回资源;
  3. 如果都没有但是执行过资源加载,则从disk中加载,否则新建任务从网络加载

最终是怎样加载资源的呢?

  1. 首先在Glide的创建时,有个重要的类——Register,大量调用了其append方法,添加默认配置:主要是ModelLoader、Encoder、Decoder等等;
  2. ModelLoader接口的静态内部类LoadData,持有DataFetcher的引用;
  3. Engine.load --> EngineJob.start(DecodeJob) --> 线程池中执行DecodeJob的run方法 --> DecodeJob.runWrapped --> getNextGenerator --> runGenerators --> DataFetcherGenerator.startNext ;
  4. 经过上面的调用流程,最终进入startNext方法,DataFetcherGenerator是一个接口,有三个子类:
    DataCacheGenerator、ResourceCacheGenerator、SourceGenerator,都大同小异的调用了ModelLoader.LoadData的DataFetcher.loadData()方法。DataFetcher的子类就多了,很显眼的有一个HttpUrlFetcher!!并且里面用的HttpUrlConnect来实现网络请求!!!

综上所述,资源加载,最终是由引擎(Engine.start)启动任务,并执行解码任务(DecodeJob.run),任务由数据访问生成器(DataFetcherGenerator.startNext)启动下一个任务方法中,有模型加载器(ModelLoader.LoadData)的数据获取器(DataFetcher.loadData)执行;

具体由哪个DataFetcher,哪个ModelLoader执行,由Register的配置根据数据类型等决定。

Glide如何处理生命周期?

这篇文章分析的较为详细:(引用文章,侵权请联系删除!)
Glide的生命周期

总结起来就是在Glide.with方法被调用时,传入的Context,如Activity\Fragment等,会创建或者从HashMap中获取一个RequestManagerFragment的实例,此实例持有ActivityFragmentLifecycle对象,其中RequestManagerFragment继承自Fragment,有Fragment的生命周期,在Fragment onStart、onStop、onDestroy触发时,调用ActivityFragmentLifecycle的相应方法,此时在即可从ActivityFragmentLifecycle获取生命周期的触发时机。
特别地,Activity中没有Fragment时,RequestManagerFragment会通过onAttach方法为Activity添加一个不可见的Fragment。

请求管理器RequestManager关联了此RequestManagerFragment实例(被观察者),并作为其Fragment生命周期的观察者,因此可以知晓生命周期,再做出对资源加载请求的启动、暂停、终止。

Glide与Picaso、Fresco的对比

  • Glide的with方法可以传Context、Activtiy、Fragment,Picasso只能传Context;
  • Glide支持GIF,Picasso不支持; Glide默认RGB565,Picasso默认ARGB8888;
  • Glide默认根据Target的尺寸缓存不同的图片,Picasso只缓存原图;
  • Glide用Java heap缓存,Fresco用C缓存; Glide无需特定的Target,Fresco必须使用指定的View。

你可能感兴趣的:(Android)