从源码角度深刻理解Glide4(下)

image

上一篇文章从源码角度深刻理解Glide(上)中,咱们已经把Glide加载图片的基本流程走了一遍,想必你已经对Glide的加载原理有了新的认识而且见识到了Glide源码的复杂逻辑,在咱们感叹Glide源码复杂的同时咱们也忽略了Glide加载图片过程的其它细节,特别是缓存方面,咱们在上一篇文章中对于缓存的处理都是跳过的,这一篇文章咱们就从Glide的缓存开始再次对Glide进行深刻理解。html

Glide缓存

  • Glide加载默认状况下能够分为三级缓存,哪三级呢?他们分别是内存、磁盘和网络。git

  • 默认状况下,Glide 会在开始一个新的图片请求以前检查如下多级的缓存:github

    • 1.活动资源 (Active Resources) - 如今是否有另外一个 View 正在展现这张图片
    • 2.内存缓存 (Memory cache) - 该图片是否最近被加载过并仍存在于内存中
    • 3.资源类型(Resource) - 该图片是否以前曾被解码、转换并写入过磁盘缓存
    • 4.数据来源 (Data) - 构建这个图片的资源是否以前曾被写入过文件缓存
  • 网络级别的加载咱们已经在上一篇文章了解了,上面列出的前两种状况则是内存缓存,后两种状况则是磁盘缓存,若是以上四种状况都不存在,Glide则会经过返回到原始资源以取回数据(原始文件,Uri, Url(网络)等)算法

缓存的key

  • 提起缓存,咱们首先要明白,Glide中缓存的图片确定不止一个,当咱们加载图片的同时,若是缓存中有咱们正在加载的图片,咱们怎么找到这个图片的缓存呢?因此为了找到对应的缓存,则每个缓存都有它对应的标识,这个标识在Glide中用接口Key来描述
/**Key 接口*/
public interface Key {
  String STRING_CHARSET_NAME = "UTF-8";
  Charset CHARSET = Charset.forName(STRING_CHARSET_NAME);
  void updateDiskCacheKey(@NonNull MessageDigest messageDigest);
  @Override
  boolean equals(Object o);
  @Override
  int hashCode();
}
复制代码

缓存Key的生成

  • 前面提到了缓存Key的接口,那这个缓存的Key实在哪里生成的,实现类又是什么呢?这咱们就要看到加载发动机Engine类的load方法
private final EngineKeyFactory keyFactory;
/**Engine类的load方法*/
public <R> LoadStatus load(GlideContext glideContext,Object model, Key signature,int width,int height,Class<?> resourceClass,Class<R> transcodeClass,Priority priority,DiskCacheStrategy diskCacheStrategy,Map<Class<?>, Transformation<?>> transformations,boolean isTransformationRequired,boolean isScaleOnlyOrNoTransform, Options options,boolean isMemoryCacheable,boolean useUnlimitedSourceExecutorPool,boolean useAnimationPool,boolean onlyRetrieveFromCache,ResourceCallback cb) {
    Util.assertMainThread();
    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;

    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);
    //省略部分代码
    ..........
  }
/**EngineKey类*/  
class EngineKey implements Key {
  private final Object model;
  private final int width;
  private final int height;
  private final Class<?> resourceClass;
  private final Class<?> transcodeClass;
  private final Key signature;
  private final Map<Class<?>, Transformation<?>> transformations;
  private final Options options;
  private int hashCode;

  EngineKey(
      Object model,
      Key signature,
      int width,
      int height,
      Map<Class<?>, Transformation<?>> transformations,
      Class<?> resourceClass,
      Class<?> transcodeClass,
      Options options) {
    this.model = Preconditions.checkNotNull(model);
    this.signature = Preconditions.checkNotNull(signature, "Signature must not be null");
    this.width = width;
    this.height = height;
    this.transformations = Preconditions.checkNotNull(transformations);
    this.resourceClass =
        Preconditions.checkNotNull(resourceClass, "Resource class must not be null");
    this.transcodeClass =
        Preconditions.checkNotNull(transcodeClass, "Transcode class must not be null");
    this.options = Preconditions.checkNotNull(options);
  }

  @Override
  public boolean equals(Object o) {
    if (o instanceof EngineKey) {
      EngineKey other = (EngineKey) o;
      return model.equals(other.model)
          && signature.equals(other.signature)
          && height == other.height
          && width == other.width
          && transformations.equals(other.transformations)
          && resourceClass.equals(other.resourceClass)
          && transcodeClass.equals(other.transcodeClass)
          && options.equals(other.options);
    }
    return false;
  }

  @Override
  public int hashCode() {
    if (hashCode == 0) {
      hashCode = model.hashCode();
      hashCode = 31 * hashCode + signature.hashCode();
      hashCode = 31 * hashCode + width;
      hashCode = 31 * hashCode + height;
      hashCode = 31 * hashCode + transformations.hashCode();
      hashCode = 31 * hashCode + resourceClass.hashCode();
      hashCode = 31 * hashCode + transcodeClass.hashCode();
      hashCode = 31 * hashCode + options.hashCode();
    }
    return hashCode;
  }
}  
复制代码
  • 由以上源码咱们知道,经过EngineKeyFactory的buildKey方法Glide建立了缓存的Key实现类EngineKey对象,由生成EngineKey对象传入的参数咱们能够明白,只要有一个参数不一样,所生成的EngineKey对象都会是不一样的。内存的速度是最快的,理所固然若是内存中有缓存的对应加载图片Glide会搜先从内存缓存中加载。

LRU

LRU算法思想

  • 从官方文档描述,咱们能够知道Glide的缓存底层实现原理算法都是LRU(Least Recently Used),字面意思为最近最少使用。算法核心思想(我的理解):在一个有限的集合中,存入缓存,每个缓存都有惟一标识,当要获取一个缓存,集合中没有则存入,有则直接从集合获取,存入缓存到集合时若是集合已经满了则找到集合中最近最少的缓存删除并存入须要存入的缓存。 这样也就有效的避免了内存溢出(OOM)的问题。
  • 接下来咱们看一张图可以更好的理解LRU算法

LRU图

  • 横线上方每一个数字表明要存入的数据,横线下方表明三个内存页(也能够理解为缓存结合),缓存集合最多能够存入三个缓存数据,则从1开始依次按照数字代码的缓存读取并存入缓存集合,首先开始时三个页内存是空的,前三个缓存数据不一样,依次存入缓存集合,当数字4在内存中并进行缓存时,根据LRU算法思想,则2和3相较于1使用时间间隔更少,因此淘汰1,缓存数据4替换1的位置,接下去同理。
  • Glide内存缓存使用的是LruCache,磁盘缓存使用的DiskLruCache,他们核心思想都是LRU算法,而缓存集合使用的是LinkedHashMap,熟悉集合框架应该都明白LinkedHashMap集成HashMap,而且LinkedHashMap保证了key的惟一性,更符合LRU算法的实现。
  • 更深刻的了解LruCache和DiskLruCache能够查看郭霖大神的解析Android DiskLruCache彻底解析,硬盘缓存的最佳方案

内存缓存

内存缓存相关API

//跳过内存缓存
RequestOptions requestOptions =new RequestOptions().skipMemoryCache(true);
Glide.with(this).load(IMAGE_URL).apply(requestOptions).into(imageView);
//Generated API 方式
GlideApp.with(this).load(IMAGE_URL).skipMemoryCache(true).into(imageView); 
//清除内存缓存,必须在主线程中调用
Glide.get(context).clearMemory();
复制代码

内存缓存源码分析

  • 内存缓存不须要你进行任何设置,它默认就是开启的,咱们再次回到Engine类的load方法
/**Engine类的load方法*/
public <R> LoadStatus load(GlideContext glideContext,Object model, Key signature,int width,int height,Class<?> resourceClass,Class<R> transcodeClass,Priority priority,DiskCacheStrategy diskCacheStrategy,Map<Class<?>, Transformation<?>> transformations,boolean isTransformationRequired,boolean isScaleOnlyOrNoTransform, Options options,boolean isMemoryCacheable,boolean useUnlimitedSourceExecutorPool,boolean useAnimationPool,boolean onlyRetrieveFromCache,ResourceCallback cb) {
    //省略部分代码
    ..........
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      return null;
    }
    //省略部分代码
    ..........
  }
复制代码
活动资源 (Active Resources)
  • 经过以上Engine类load的源码,首先调用loadFromActiveResources方法来从内存中获取缓存
private final ActiveResources activeResources;
/**Engine类的loadFromActiveResources方法*/
@Nullable
  private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {
      return null;
    }
    EngineResource<?> active = activeResources.get(key);
    if (active != null) {
      active.acquire();
    }
    return active;
  }
/**ActiveResources类*/
final class ActiveResources {
     @VisibleForTesting
  final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
   //省略部分代码
    ........
   @VisibleForTesting
  static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
    //省略部分代码
    ........
  }
}
/**Engine类的onEngineJobComplete方法*/
@SuppressWarnings("unchecked")
  @Override
  public void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    Util.assertMainThread();
    // A null resource indicates that the load failed, usually due to an exception.
    if (resource != null) {
      resource.setResourceListener(key, this);

      if (resource.isCacheable()) {
        activeResources.activate(key, resource);
      }
    }

    jobs.removeIfCurrent(key, engineJob);
  }

/**RequestOptions类的skipMemoryCache方法*/
public RequestOptions skipMemoryCache(boolean skip) {
    if (isAutoCloneEnabled) {
      return clone().skipMemoryCache(true);
    }
    this.isCacheable = !skip;
    fields |= IS_CACHEABLE;
    return selfOrThrowIfLocked();
  }
复制代码
  • 经过以上源码, 这里须要分几步来解读,首先若是是第一次加载,确定没有内存缓存,因此若是第一次加载成功,则在加载成功以后调用了Engine对象的onEngineJobComplete方法,并在该方法中将加载成功的resource经过ActiveResources对象的activate方法保存在其内部维护的弱引用(WeakReference)HashMap中。下次再加载相同的资源,当你设置了skipMemoryCache(true),则代表你不想使用内存缓存,这时候Glide再次加载相同资源的时候则会跳过内存缓存的加载,不然能够从ActiveResources对象中获取,若是内存资源没被回收的话(关于弱引用的一下描述能够看看我之前写的一篇文章Android 学习笔记之图片三级缓存)。若是该弱引用资源被回收了(GC),则下一步就到内存中寻找是否有该资源的缓存。
内存缓存 (Memory cache)
  • 接着回到Engine类的load方法,若是弱引用缓存资源已经被回收,则调用loadFromCache方法在内存缓存中查找缓存资源
/**Engine类的load方法*/
public <R> LoadStatus load(GlideContext glideContext,Object model, Key signature,int width,int height,Class<?> resourceClass,Class<R> transcodeClass,Priority priority,DiskCacheStrategy diskCacheStrategy,Map<Class<?>, Transformation<?>> transformations,boolean isTransformationRequired,boolean isScaleOnlyOrNoTransform, Options options,boolean isMemoryCacheable,boolean useUnlimitedSourceExecutorPool,boolean useAnimationPool,boolean onlyRetrieveFromCache,ResourceCallback cb) {
    //省略部分代码
    ..........
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return null;
    }
    //省略部分代码
    ..........
  }
/**Engine类的loadFromCache方法*/
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {
      return null;
    }

    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
      cached.acquire();
      activeResources.activate(key, cached);
    }
    return cached;
  }
/**Engine类的getEngineResourceFromCache方法*/
private final MemoryCache cache;
private EngineResource<?> getEngineResourceFromCache(Key key) {
    Resource<?> cached = cache.remove(key);

    final EngineResource<?> result;
    if (cached == null) {
      result = null;
    } else if (cached instanceof EngineResource) {
      // Save an object allocation if we've cached an EngineResource (the typical case). result = (EngineResource<?>) cached; } else { result = new EngineResource<>(cached, true /*isMemoryCacheable*/, true /*isRecyclable*/); } return result; } /**GlideBuilder类的build方法*/ private MemoryCache cache; @NonNull Glide build(@NonNull Context context) { //省略部分代码 .......... if (memoryCache == null) { memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize()); } if (diskCacheFactory == null) { diskCacheFactory = new InternalCacheDiskCacheFactory(context); } if (engine == null) { engine = new Engine( memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor, GlideExecutor.newUnlimitedSourceExecutor(), GlideExecutor.newAnimationExecutor(), isActiveResourceRetentionAllowed); } //省略部分代码 .......... } /**LruResourceCache类的实现继承关系*/ public class LruResourceCache extends LruCache<Key, Resource<?>> implements MemoryCache{......} 复制代码
  • 经过以上源码,在loadFromCache一样也判断了Glide是否设置了skipMemoryCache(true)方法,没有设置则调用getEngineResourceFromCache方法,在该方法中咱们能够看到cache对象就是MemoryCache对象,而该对象实际是一个接口,他的实现类是LruResourceCache,该对象咱们前面在GlideBuilder的build方法中进行了新建(在第一步with方法中调用了Glide.get方法,在get方法中初始化Glide调用了在GlideBuilder的build方法),这里也就说明Glide的内存缓存仍是使用LruCache来实现,这里若是获取到了内存缓存,则获取内容缓存的同时移除该缓存,并在loadFromCache方法中将该资源标记为正在使用同时加入在弱引用中。这样在ListView或者Recyclerview中加载图片则下次加载首先从弱引用Map中获取缓存资源,而且标志当前资源正在使用,能够防止该资源被LRU算法回收掉。
内存缓存写入
  • 前面咱们只是分析了如何获取内存缓存,而内存缓存又是在哪里写入的呢?根据前面分析,首先获取在弱引用Map中的缓存资源,而前面咱们在分析活动资源(Active Resources)时候已经说过是在onEngineJobComplete放中往弱引用Map存放缓存资源,而 onEngineJobComplete方法是在哪里调用呢,这咱们就要回想起上一篇文章中咱们再网络加载图片成功后腰切换在主线程回调来显示图片,也就是EngineJob对象的handleResultOnMainThread方法
/**EngineJob类的handleResultOnMainThread方法*/
@Synthetic
  void handleResultOnMainThread() {
    //省略部分代码
    ..........
    engineResource = engineResourceFactory.build(resource, isCacheable);
    hasResource = true;
    //省略部分代码
    ..........
    engineResource.acquire();
    listener.onEngineJobComplete(this, key, engineResource);
    engineResource.release();
    //省略部分代码
    ..........
  }
/**EngineJob类的EngineResourceFactory内部类*/  
@VisibleForTesting
  static class EngineResourceFactory {
    public <R> EngineResource<R> build(Resource<R> resource, boolean isMemoryCacheable) {
      return new EngineResource<>(resource, isMemoryCacheable, /*isRecyclable=*/ true);
    }
  }
/**Engine类的onEngineJobComplete方法*/
@SuppressWarnings("unchecked")
  @Override
  public void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    //省略部分代码
    ..........
    if (resource != null) {
      resource.setResourceListener(key, this);
      if (resource.isCacheable()) {
        activeResources.activate(key, resource);
      }
    }
    //省略部分代码
    ..........
  }  
复制代码
  • 经过以上源码,EngineJob类的handleResultOnMainThread方法首先构建了获取好的包含图片的资源,标记当前资源正在使用,经过listener.onEngineJobComplete回调,而listener就是Engine对象,也就到了Engine类的onEngineJobComplete方法,并在该方法中存入了图片资源到弱引用Map中。
  • 上面我是分析了弱引用资源的缓存存入,接着咱们看看内存缓存是在哪里存入的,在次看回handleResultOnMainThread方法,咱们看到onEngineJobComplete回调先后分别调用了EngineResource对象的acquire方法和release方法
/**EngineResource类的acquire方法*/
void acquire() {
    if (isRecycled) {
      throw new IllegalStateException("Cannot acquire a recycled resource");
    }
    if (!Looper.getMainLooper().equals(Looper.myLooper())) {
      throw new IllegalThreadStateException("Must call acquire on the main thread");
    }
    ++acquired;
  }
/**EngineResource类的release方法*/
  void release() {
    if (acquired <= 0) {
      throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
    }
    if (!Looper.getMainLooper().equals(Looper.myLooper())) {
      throw new IllegalThreadStateException("Must call release on the main thread");
    }
    if (--acquired == 0) {
      listener.onResourceReleased(key, this);
    }
  }
/**Engine类的onResourceReleased方法*/
@Override
  public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    Util.assertMainThread();
    activeResources.deactivate(cacheKey);
    if (resource.isCacheable()) {
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource);
    }
  }  
复制代码
  • 经过以上源码,其实咱们应该能恍然大悟,Glide的内存缓存存入其实就是经过一个acquired变量来进行控制,若是当前弱引用资源再也不使用,也就是acquired等于零的时候,则调用回调listener.onResourceReleased(listener就是Engine对象),在onResourceReleased方法中移除了弱引用资源资源,而且没有设置skipMemoryCache(true),则经过cache.put存入内存缓存。
  • 总的来讲Glide的内存缓存主要是结合了弱引用和内存来实现的。
Glide内存缓存机制示意图

Glide内存缓存机制示意图

磁盘缓存

  • 说去磁盘缓存,上一篇文章咱们在简单使用Glide的例子中就已经使用了Glide的磁盘缓存
RequestOptions requestOptions = new RequestOptions()
         .diskCacheStrategy(DiskCacheStrategy.NONE);//不使用缓存
     Glide.with(Context).load(IMAGE_URL).apply(requestOptions).into(mImageView);
复制代码
  • 既然知道如何使用Glide的磁盘缓存,首先咱们要了解Glide4中给我提供了哪几种磁盘缓存策略

磁盘缓存策略

  • 1.DiskCacheStrategy.NONE: 表示不使用磁盘缓存
  • 2.DiskCacheStrategy.DATA: 表示磁盘缓存只缓存原始加载的图片
  • DiskCacheStrategy.RESOURCE: 表示磁盘缓存只缓存通过解码转换后的图片
  • DiskCacheStrategy.ALL: 表示磁盘缓存既缓存原始图片,也缓存通过解码转换后的图片
  • DiskCacheStrategy.AUTOMATIC: 表示让Glide根据图片资源智能地选择使用哪种磁盘缓存策略,该选项也是咱们在不进行手动设置的时候Glide的默认设置

磁盘缓存源码分析

  • 不知你是否还记得上一篇文章中在加载图片的时候咱们是在开启子线程任务在线程池中进行的,咱们来回顾一下
/**DecodeJob类的start方法*/
public void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    executor.execute(decodeJob);
  }
/**DecodeJob类的willDecodeFromCache方法*/ 
 boolean willDecodeFromCache() {
    Stage firstStage = getNextStage(Stage.INITIALIZE);
    return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
  } 
/**DecodeJob类的getNextStage方法*/   
private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
     //省略部分代码
    ......
    }
  }
/**DiskCacheStrategy类的ALL对象*/     
public static final DiskCacheStrategy ALL = new DiskCacheStrategy() {
    @Override
    public boolean isDataCacheable(DataSource dataSource) {
      return dataSource == DataSource.REMOTE;
    }

    @Override
    public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,
        EncodeStrategy encodeStrategy) {
      return dataSource != DataSource.RESOURCE_DISK_CACHE && dataSource != DataSource.MEMORY_CACHE;
    }

    @Override
    public boolean decodeCachedResource() {
      return true;
    }

    @Override
    public boolean decodeCachedData() {
      return true;
    }
  };  
/**GlideBuilder类的build方法*/  
@NonNull
  Glide build(@NonNull Context context) {
    //省略部分代码
    ......
    if (diskCacheExecutor == null) {
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }
    //省略部分代码
    ......
}
/**GlideExecutor类的newDiskCacheExecutor方法*/ 
private static final int DEFAULT_DISK_CACHE_EXECUTOR_THREADS = 1;
public static GlideExecutor newDiskCacheExecutor() {
    return newDiskCacheExecutor(
        DEFAULT_DISK_CACHE_EXECUTOR_THREADS,
        DEFAULT_DISK_CACHE_EXECUTOR_NAME,
        UncaughtThrowableStrategy.DEFAULT);
  }
/**GlideExecutor类的newDiskCacheExecutor方法*/ 
 public static GlideExecutor newDiskCacheExecutor(
      int threadCount, String name, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
    return new GlideExecutor(
        new ThreadPoolExecutor(
            threadCount /* corePoolSize */,
            threadCount /* maximumPoolSize */,
            0 /* keepAliveTime */,
            TimeUnit.MILLISECONDS,
            new PriorityBlockingQueue<Runnable>(),
            new DefaultThreadFactory(name, uncaughtThrowableStrategy, true)));
  }  
复制代码
  • 经过以上源码,能够分两个步骤来进行解读:
    • 第一步在DecodeJob对象的start方法开启子线程来加载图片,这里使用了线程池,经过willDecodeFromCache方法和getNextStage放结合,主要经过Stage枚举来判断当前使用的缓存策略,而缓存策略的设置则经过DiskCacheStrategy对象的decodeCachedResource和decodeCachedData方法来进行设置,而这两个方法在DiskCacheStrategy抽象类都是抽象方法,而他们的实现就是咱们前面提到的Glide磁盘缓存的五种策略,上面代码中列出其中一种ALL代码,decodeCachedResource和decodeCachedData方法都返回ture,也就说明磁盘缓存既缓存原始图片,也缓存通过解码转换后的图片;若是decodeCachedResource返回false和decodeCachedData方法返回true,也就表明DATA策略,磁盘缓存只缓存原始加载的图片,其余同理
    • 第二步经过前面对设置策略的判断,若是有缓存策略,则拿到的线程池就是磁盘缓存加载的线程池(线程池的理解能够看看我之前写的一篇文章),该线程的初始化仍是在GlideExecutor对象的build方法中,经过以上源码,该线程池只有惟一一个核心线程,这就保证全部执行的的任务都在这一个线程中执行,而且是顺序执行,也就不用在考虑线程同步的问题了。
  • 根据前面官网的说明,无论是内存缓存仍是磁盘缓存,都是使用LRU,接着看看Glide磁盘缓存在哪获取LRU对象,仍是得看到GlideBuilder对象的build方法
/**GlideBuilder类的build方法*/  
private DiskCache.Factory diskCacheFactory;
@NonNull
  Glide build(@NonNull Context context) {
    //省略部分代码
    ..........
    if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }
    //省略部分代码
    ..........
}
/**InternalCacheDiskCacheFactory类的继承关系*/
public final class InternalCacheDiskCacheFactory extends DiskLruCacheFactory {
   //省略实现代码
    ..........  
}
/**DiskLruCacheFactory类的部分代码*/
public class DiskLruCacheFactory implements DiskCache.Factory {
   //省略部分代码
    ..........
    @Override
  public DiskCache build() {
    File cacheDir = cacheDirectoryGetter.getCacheDirectory();
    //省略部分代码
    ..........
    return DiskLruCacheWrapper.create(cacheDir, diskCacheSize);
  }
}
/**DiskLruCacheWrapper类的部分代码*/
public class DiskLruCacheWrapper implements DiskCache {
//省略部分代码
    ..........
   private synchronized DiskLruCache getDiskCache() throws IOException {
    if (diskLruCache == null) {
      diskLruCache = DiskLruCache.open(directory, APP_VERSION, VALUE_COUNT, maxSize);
    }
    return diskLruCache;
  } 
  //省略部分代码
    ..........
}
/**DiskLruCache类的部分代码*/
public final class DiskLruCache implements Closeable {
    //省略部分代码
    ..........
    public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
      throws IOException {
          DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
          //省略部分代码
          ..........
          return cache;
      }
      //省略部分代码
      ..........
}

复制代码
  • 经过以上源码,咱们在GlideBuilder对象的build方法中已经新建了InternalCacheDiskCacheFactory对象,也就是DiskLruCacheFactory对象,一样该对象已经被咱们传入Engine对象的构造方法中,最终包装成LazyDiskCacheProvider对象(该对象代码就不贴出了),因此只要调用DiskLruCacheFactory对象的build方法就可以最终获取到DiskLruCache对象,该对象是Glide本身实现的,可是其原理和谷歌官方推荐的DiskLruCache也差不了太多,核心仍是使用LRU算法来实现磁盘缓存。
资源类型(Resource)
  • 根据前面分分析,假定没有内存缓存,而是由磁盘缓存,则结合前面分析咱们获得了磁盘缓存处理的线程池,也得到枚举Stage是RESOURCE_CACHE或DATA_CACHE,则在DecodeJob对象getNextGenerator方法,咱们就能获得对应的Generator
/**DecodeJob的getNextGenerator方法*/
private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        return new ResourceCacheGenerator(decodeHelper, this);
      case DATA_CACHE:
        return new DataCacheGenerator(decodeHelper, this);
      case SOURCE:
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }
复制代码
  • 经过getNextGenerator方法的源码,若是以前设置磁盘缓存策略为DiskCacheStrategy.RESOURCE,则应该对应的就是枚举Stage.RESOURCE_CACHE,也就是说接下来使用的资源Generator是ResourceCacheGenerator,结合上一篇文章,咱们分析网络加载流程是这里获取的是SourceGenerator,咱们接着来看ResourceCacheGenerator的startNext()方法
/** ResourceCacheGenerator类的startNext方法*/
  @SuppressWarnings("PMD.CollapsibleIfStatements")
  @Override
  public boolean startNext() {
     //省略部分代码
      ..........
      currentKey =
          new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
              helper.getArrayPool(),
              sourceId,
              helper.getSignature(),
              helper.getWidth(),
              helper.getHeight(),
              transformation,
              resourceClass,
              helper.getOptions());
      cacheFile = helper.getDiskCache().get(currentKey);
      if (cacheFile != null) {
        sourceKey = sourceId;
        modelLoaders = helper.getModelLoaders(cacheFile);
        modelLoaderIndex = 0;
      }
    }

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
      loadData = modelLoader.buildLoadData(cacheFile,
          helper.getWidth(), helper.getHeight(), helper.getOptions());
      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }
/**DecodeHelper类的getDiskCache方法*/
 DiskCache getDiskCache() {
    return diskCacheProvider.getDiskCache();
  }
/** LazyDiskCacheProvider类的getDiskCache方法 */
@Override
    public DiskCache getDiskCache() {
      if (diskCache == null) {
        synchronized (this) {
          if (diskCache == null) {
            diskCache = factory.build();
          }
          if (diskCache == null) {
            diskCache = new DiskCacheAdapter();
          }
        }
      }
      return diskCache;
    }  
复制代码
  • 经过以上源码其实已经很清晰,首先仍是获取缓存的惟一key,而后helper.getDiskCache().get(currentKey)这一句话就是获取缓存,helper对象就是DecodeHelper,它的getDiskCache方法获取的对象也就是前面提到的包含DiskLruCacheFactory对象的LazyDiskCacheProvider对象,而LazyDiskCacheProvider对象的getDiskCache方法调用了factory.build(),factory对象DiskLruCacheFactory,也就是获取了咱们前面所说的DiskLruCache对象
  • 接着继续看数据返回走的流程仍是经过回调通cb.onDataFetcherReady将获取的缓存资源传递到DecodeJob,由DecodeJob继续执行剩余图片显示步骤,大体流程和网络加载差很少,这里就不进行讨论了
/** ResourceCacheGenerator类的startNext方法*/
 @Override
  public void onDataReady(Object data) {
    cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE,
        currentKey);
  }
复制代码
数据来源 (Data)
  • 同理资源类型(Resource),则设置磁盘缓存策略为DiskCacheStrategy.DATA,则应该对应的就是枚举Stage.DATA_CACHE,使用的资源Generator是DataCacheGenerator,因此直接看看DataCacheGenerator的startNext()方法,该方法源码以下,一样是根据key经过DiskLruCache对象来获取磁盘缓存(DATA),数据返回走的流程仍是经过回调通cb.onDataFetcherReady将获取的缓存资源传递到DecodeJob,由DecodeJob继续执行剩余图片显示
/** DataCacheGenerator类的startNext方法*/
 @Override
  public boolean startNext() {
      //省略部分代码
      ..........
      Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
      cacheFile = helper.getDiskCache().get(originalKey);
      if (cacheFile != null) {
        this.sourceKey = sourceId;
        modelLoaders = helper.getModelLoaders(cacheFile);
        modelLoaderIndex = 0;
      }
    }

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
      loadData =
          modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
              helper.getOptions());
      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }
/** DataCacheGenerator类的onDataReady方法*/  
@Override
  public void onDataReady(Object data) {
    cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
  }  
复制代码
磁盘缓存数据存入
  • 前面咱们只是了解了磁盘缓存的获取,磁盘缓存又是在哪里存入的,接着往下看。
  • 根据上一篇文章的分析,加载图片会走到DecodeJob对象的decodeFromRetrievedData方法
/** DecodeJob类的decodeFromRetrievedData方法*/  
private void decodeFromRetrievedData() {
    //省略部分代码
      ..........
    notifyEncodeAndRelease(resource, currentDataSource);
    //省略部分代码
      ..........
} 
/** DecodeJob类的notifyEncodeAndRelease方法*/  
private final DeferredEncodeManager<?> deferredEncodeManager = new DeferredEncodeManager<>();
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
//省略部分代码
      ..........
    stage = Stage.ENCODE;
    try {
      if (deferredEncodeManager.hasResourceToEncode()) {
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } 
    //省略部分代码
    ..........
  }
/** DeferredEncodeManager类的encode方法**/
void encode(DiskCacheProvider diskCacheProvider, Options options) {
      GlideTrace.beginSection("DecodeJob.encode");
      try {
        diskCacheProvider.getDiskCache().put(key,
            new DataCacheWriter<>(encoder, toEncode, options));
      } finally {
        toEncode.unlock();
        GlideTrace.endSection();
      }
    } 
复制代码
  • 经过以上源码能够看到DecodeJob对象的decodeFromRetrievedData方法经过调用notifyEncodeAndRelease方法,在该方法中调用了内部类DeferredEncodeManager的encode方法存入了磁盘缓存,这里存入的是转换后的磁盘缓存(Resource)。
  • 原始数据也就是SourceGenerator第一次网络下载成功以后获取的图片数据,以后再作磁盘缓存,因此再次回到看到SourceGenerator的onDataReady方法
/**SourceGenerator类的onDataReady方法**/ 
private Object dataToCache;
@Override
  public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
          loadData.fetcher.getDataSource(), originalKey);
    }
  }  
/**SourceGenerator类的startNext方法**/
@Override
  public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
   //省略部分代码
  ..........
  }
/**SourceGenerator类的cacheData方法**/  
private void cacheData(Object dataToCache) {
    long startTime = LogTime.getLogTime();
    try {
      Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
      DataCacheWriter<Object> writer =
          new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
      originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
      helper.getDiskCache().put(originalKey, writer);
    } 
    //省略部分代码
    ..........
    sourceCacheGenerator =
        new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
  }
/**DecodeJob类的reschedule方法**/ 
@Override
  public void reschedule() {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);
  }
/**Engine类的reschedule方法**/   
 @Override
  public void reschedule(DecodeJob<?> job) {
    getActiveSourceExecutor().execute(job);
  }  
复制代码
  • 经过以上源码,其实逻辑已经很清晰,会让你有“柳暗花明又一村”的感受,onDataReady网络请求成功而且设置了缓存策略,则将图片资源赋值给Object类型的dataToCache,执行回调cb.reschedule,cb就是DecodeJob对象,因此接着执行了DecodeJob对象的reschedule方法,该方法再次执行回调也就是执行了Engine对象的reschedule方法,该方法再次执行DecodeJob,也就会再次触发SourceGenerator类的startNext方法,该方法首先判断了Object类型的dataToCache是否有值,前面分析该对象已经赋值,因此就进入到SourceGenerator对象的cacheData方法存入了咱们的原始下载图片的缓存。

仅从缓存加载图片

  • 前面我基本把Glide的缓存模块梳理了一遍,可是还差个东西,那就是若是我只想Glide加载缓存呢?这种需求仍是有的,好比说咱们在有些应用看到的省流量模式,不就是正好对应这个需求,不要紧Gldie也已经为咱们考虑到了,那就是onlyRetrieveFromCache(true),只要设置了这个,图片在内存缓存或在磁盘缓存中就会被加载出来,而没有缓存,则这一次加载失败。
  • 咱们看看如何使用
RequestOptions requestOptions = new RequestOptions().onlyRetrieveFromCache(true);
Glide.with(this).load(IMAGE_URL).apply(requestOptions).into(mImageView);
//Generated API 方式        
GlideApp.with(this)
  .load(url)
  .diskCacheStrategy(DiskCacheStrategy.ALL)
  .into(mImageView);        
复制代码
  • 使用起来仍是很方便的,只要设置onlyRetrieveFromCache(true)方法就行,而它的原理也其实也很简单,咱们再次回到DecodeJob对象的getNextStage方法,若是前面获取了缓存,则相应获得对应的Generator加载图片,若是获取不到缓存,则枚举Stage.FINISHED,DecodeJob对象的getNextGenerator方法则会返回null。(以下代码所示)
/**DecodeJob类的getNextStage方法**/ 
private Stage getNextStage(Stage current) {
    switch (current) {
    //省略部分代码
    ..........
      case DATA_CACHE:
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
    }
  }
/**DecodeJob类的getNextGenerator方法**/   
private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
    //省略部分代码
    ..........
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }  
复制代码
Glide磁盘缓存机制示意图

Glide磁盘缓存机制示意图

Glide缓存小结

  • 经过前面对Glide缓存的分析,让我再次认识到Glide的强大,使用时只是简单的几个方法设置或者不设置,Glide都可以在背后依靠其复杂的逻辑为咱们快速的加载出图片并显示,缓存还有一些细节好比能够自定义key等,这里就不进行展开了,有兴趣的能够自行研究。

Glide 回调与监听

图片加载成功回调原理

  • 由上一篇文章分析,咱们来回顾一下图片加载成功以后的逻辑。数据加载成功以后切换主线程最终调用SingleRequest类的onResourceReady方法,在该方法中加载成功的数据经过target.onResourceReady方法将数据加载出来,target就是DrawableImageViewTarget对象,他继续了实现了Target接口的基类ImageViewTarget,因此调用它实现的onResourceReady方法或者父类实现的onResourceReady方法就实现了加载成功数据的回调,并由DrawableImageViewTarget对象显示加载成功的图片,这就是数据加载成功回调原理。
/**SingleRequest类的onResourceReady方法**/    
@Nullable 
private List<RequestListener<R>> requestListeners;
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    //省略部分代码
    ..........
    isCallingCallbacks = true;
    try {
      boolean anyListenerHandledUpdatingTarget = false;
      //省略部分代码
     ..........

      if (!anyListenerHandledUpdatingTarget) {
        Transition<? super R> animation =
            animationFactory.build(dataSource, isFirstResource);
        target.onResourceReady(result, animation);
      }
    } 
    //省略部分代码
    ..........
  }
/**Target 接口**/      
public interface Target<R> extends LifecycleListener {}
/**ImageViewTarget类的onResourceReady方法**/ 
@Override
  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      maybeUpdateAnimatable(resource);
    }
  }
/**ImageViewTarget类的setResourceInternal方法**/ 
private void setResourceInternal(@Nullable Z resource) {
    setResource(resource);
    maybeUpdateAnimatable(resource);
  }
DrawableImageViewTarget
/**DrawableImageViewTarget类的setResource方法**/ 
 @Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }  
复制代码

Glide监听(listener)

  • 再次看看Glide监听(listener)的例子
Glide.with(this).load(IMAGE_URL).listener(new RequestListener<Drawable>() {
           @Override
           public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
               return false;
           }

           @Override
           public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
               return false;
           }
       }).into(mImageView);
复制代码
  • Glide监听的实现一样仍是基于咱们上面分析的SingleRequest对象的onResourceReady方法,使用的时候调用RequestBuilder对象的listener方法,传入的RequestListener对象加入到requestListeners,这样在SingleRequest对象的onResourceReady方法中遍历requestListeners,来回调listener.onResourceReady方法,布尔类型的anyListenerHandledUpdatingTarget则接收回调listener.onResourceReady方法的返回值,若是返回true,则不会执会往下执行,则接着的into方法就不会被触发,说明咱们本身在监听中处理,返回false则不拦截。
/**RequestBuilder类的listener方法**/ 
@Nullable 
private List<RequestListener<TranscodeType>> requestListeners;
  public RequestBuilder<TranscodeType> listener(
      @Nullable RequestListener<TranscodeType> requestListener) {
    this.requestListeners = null;
    return addListener(requestListener);
  }
/**RequestBuilder类的addListener方法**/  
  public RequestBuilder<TranscodeType> addListener(
      @Nullable RequestListener<TranscodeType> requestListener) {
    if (requestListener != null) {
      if (this.requestListeners == null) {
        this.requestListeners = new ArrayList<>();
      }
      this.requestListeners.add(requestListener);
    }
    return this;
  }

/**SingleRequest类的onResourceReady方法**/    
@Nullable 
private List<RequestListener<R>> requestListeners;
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    //省略部分代码
    ..........
    isCallingCallbacks = true;
    try {
      boolean anyListenerHandledUpdatingTarget = false;
      if (requestListeners != null) {
        for (RequestListener<R> listener : requestListeners) {
          anyListenerHandledUpdatingTarget |=
              listener.onResourceReady(result, model, target, dataSource, isFirstResource);
        }
      }
      anyListenerHandledUpdatingTarget |=
          targetListener != null
              && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);

      if (!anyListenerHandledUpdatingTarget) {
        Transition<? super R> animation =
            animationFactory.build(dataSource, isFirstResource);
        target.onResourceReady(result, animation);
      }
    } 
    //省略部分代码
    ..........
  }
复制代码

Target(目标)

  • Target在Glide中至关于中间人的做用,在图片的展现起到承上启下的功效,首先看看Target接口的继承关系图

Target继承关系图

  • 经过该图,咱们能够把Target分为三类,一种是简单的Target,一种是加载到特定View的Target(ViewTarget),还有一种是FutureTarget,能够知道异步执行的结果,获得缓存文件
  • 上一篇文章分析into方法时咱们是分析into(ImageView)这个方法开始的,它内部仍是会获得特定的Target对象,也就是咱们一直说的DrawableImageViewTarget,而他是属于ViewTarget的子类

简单的Target(SimpleTarget)

  • SimpleTarget实际上是在给咱们更灵活的加载到各类各样对象准备的,只要指定咱们加载获取的是什么对象asBitmap(),就能使用SimpleTarge或者集成它的咱们自定义的对象,在其中经过获取的Bitmap显示在对应的控件上,好比上一篇文章例子提到的NotifivationTarget,就是加载到指定的Notifivation中,灵活加载。
//注意须要指定Glide的加载类型asBitmap,不指定Target不知道自己是是类型的Target
Glide.with(this).asBitmap().load(IMAGE_URL).into(new SimpleTarget<Bitmap>() {
            @Override
            public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
            //加载完成已经在主线程
                mImageView.setImageBitmap(resource);
            }
        });
复制代码

特定View的Target(ViewTarget)

  • 由DrawableImageViewTarget和BitmapImageViewTarget咱们就能够知道这是为了避免同类型的图片资源准备的Target,可是还有一种需求,就是若是咱们传入是要加载图片资源的View,可是该View不被Glide支持,目前into方法支持传入ImageView,不要紧,ViewTarget能够帮上忙,好比咱们须要加载到RelativeLayout
/**
 * @author maoqitian
 * @Description: 自定义RelativeLayout
 * @date 2019/2/18 0018 19:51
 */

public class MyView extends RelativeLayout {
    private ViewTarget<MyView, Drawable> viewTarget;
    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        viewTarget =new ViewTarget<MyView, Drawable>(this) {
            @Override
            public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
                setBackground(resource);
            }
        };
    }

    public ViewTarget<MyView, Drawable> getViewTarget() {
        return viewTarget;
    }
}

//使用Glide加载
MyView rl_view = findViewById(R.id.rl_view);
Glide.with(this).load(IMAGE_URL).into(rl_view.getViewTarget());
复制代码

FutureTarget

  • FutureTarget的一大用处就是能够获得缓存文件
new Thread(new Runnable() {
            @Override
            public void run() {
                FutureTarget<File> target = null;
                RequestManager requestManager = Glide.with(MainActivity.this);
                try {
                    target = requestManager
                            .downloadOnly()
                            .load(IMAGE_URL)
                            .submit();
                    final File downloadedFile = target.get();
                    Log.i(TAG,"缓存文件路径"+downloadedFile.getAbsolutePath());
                } catch (ExecutionException | InterruptedException e) {

                } finally {
                    if (target != null) {
                        target.cancel(true); // mayInterruptIfRunning
                    }
                }
            }
        }).start();
复制代码

preload(预加载)

  • 如何使用
Glide.with(this).load(IMAGE_URL).preload();
复制代码
  • 预加载其实也是属于Target的范围,只是他加载的对象为空而已,也就是没有加载目标
/**RequestBuilder类的preload方法**/
 @NonNull
  public Target<TranscodeType> preload() {
    return preload(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
  }
/**RequestBuilder类的preload方法**/ 
@NonNull
  public Target<TranscodeType> preload(int width, int height) {
    final PreloadTarget<TranscodeType> target = PreloadTarget.obtain(requestManager, width, height);
    return into(target);
  }
/**RequestBuilder类的onResourceReady方法**/   
public final class PreloadTarget<Z> extends SimpleTarget<Z> {

private static final Handler HANDLER = new Handler(Looper.getMainLooper(), new Callback() {
    @Override
    public boolean handleMessage(Message message) {
      if (message.what == MESSAGE_CLEAR) {
        ((PreloadTarget<?>) message.obj).clear();
        return true;
      }
      return false;
    }
  });

//省略部分代码
    ..........
public static <Z> PreloadTarget<Z> obtain(RequestManager requestManager, int width, int height) {
    return new PreloadTarget<>(requestManager, width, height);
  } 

@Override
  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    HANDLER.obtainMessage(MESSAGE_CLEAR, this).sendToTarget();
  }
  //省略部分代码
    ..........
}  
复制代码
  • 经过以上源码,逻辑已经很是清晰,Glide的preload方法里使用的继承SimpleTarget的PreloadTarget对象来做为Target,在它的onResourceReady方法中并无任何的加载操做,只是调用了Handler来释放资源,到这里也许你会有疑惑,不是说预加载么,怎么不加载。哈哈,其实到onResourceReady方法被调用通过前面的分析Glide已经走完缓存的全部逻辑,那就很容易理解了,预加载只是把图片加载到缓存当中,没有进行其余操做,天然是预加载,而且加载完成以后释放了资源。

Generated API

  • Generated API说白了就是Glide使用注解处理器生成一个API(GlideApp),该API能够代替Glide帮助咱们完成图片加载。
  • Generated API 目前仅能够在 Application 模块内使用,使用Generated API一方面在Application 模块中可将经常使用的选项组打包成一个选项在 Generated API 中使用,另外一方面能够为Generated API 扩展自定义选项(扩展咱们自定义的功能方法)。
  • 在上一篇文章中例子中咱们能够看到使用Generated API以后使用Glide的方式基本上和Glide3的用法同样流式API使用,先来回顾一下如何使用Generated API
//在app下的gradle添加Glide注解处理器的依赖
dependencies {
  annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
}
//新建一个类集成AppGlideModule并添加上@GlideModule注解,从新rebuild项目就可使用GlideApp了
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {}

复制代码
  • 通过上的代码的操做,经过Glide注解处理器已经给咱们生成了GlideApp类
/**GlideApp类部分代码**/ 
public final class GlideApp {
    //省略部分代码
    ..........
    @NonNull
    public static GlideRequests with(@NonNull Context context) {
    return (GlideRequests) Glide.with(context);
  }
  //省略部分代码
    ..........
}
/**GlideApp类部分代码**/ 
public class GlideRequest<TranscodeType> extends RequestBuilder<TranscodeType> implements Cloneable {
//省略部分代码
    ..........
  @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;
  }
  //省略部分代码
    ..........
}
/**RequestBuilder类的getMutableOptions方法**/ 
protected RequestOptions getMutableOptions() {
    return defaultRequestOptions == this.requestOptions
        ? this.requestOptions.clone() : this.requestOptions;
  }
复制代码
  • 经过以上源码,能够发现,GlideApp对象的with方法返回的是GlideRequests对象,GlideRequests对象继承的是RequestBuilder,这时应该又是豁然开朗的感受,GlideApp可以适应流式API,其实就是对RequestBuilder包装了一层,GlideRequests对象经过其父类RequestBuilder对象的getMutableOptions方法获取到requestOptions,而后在相应的方法中操做requestOptions以达到可使用流式API的功能。

GlideExtension

  • GlideExtension字面意思就是Glide扩展,它是一个做用于类上的注解,任何扩展 Glide API 的类都必须使用这个注解来标记,不然其中被注解的方法就会被忽略。 被 @GlideExtension 注解的类应以工具类的思惟编写。这种类应该有一个私有的、空的构造方法,应为 final 类型,而且仅包含静态方法。

@GlideOption

  • GlideOption注解是用来扩展RequestOptions,扩展功能方法第一个参数必须是RequestOptions。下面咱们经过设置一个扩展默认设置占位符和错误符方法的例子来讲明GlideOption注解。
/**
 * @author maoqitian
 * @Description: GlideApp 功能扩展类
 * @date 2019/2/19 0019 12:51
 */
@GlideExtension
public class MyGlideExtension {

    private MyGlideExtension() {
    }

    //能够为方法任意添加参数,但要保证第一个参数为 RequestOptions
    /**
     * 设置通用的加载占位图和错误加载图
     * @param options
     */
    @GlideOption
    public static void normalPlaceholder(RequestOptions options) {
        options.placeholder(R.drawable.ic_cloud_download_black_24dp).error(R.drawable.ic_error_black_24dp);
    }
}
/**GlideOptions类中生成对应的方法**/
/**
   * @see MyGlideExtension#normalPlaceholder(RequestOptions)
   */
  @CheckResult
  @NonNull
  public GlideOptions normalPlaceholder() {
    if (isAutoCloneEnabled()) {
      return clone().normalPlaceholder();
    }
    MyGlideExtension.normalPlaceholder(this);
    return this;
  }
/**GlideRequest类中生成对应的方法**/  
/**
   * @see GlideOptions#normalPlaceholder()
   */
  @CheckResult
  @NonNull
  public GlideRequest<TranscodeType> normalPlaceholder() {
    if (getMutableOptions() instanceof GlideOptions) {
      this.requestOptions = ((GlideOptions) getMutableOptions()).normalPlaceholder();
    } else {
      this.requestOptions = new GlideOptions().apply(this.requestOptions).normalPlaceholder();
    }
    return this;
  }
复制代码
  • 如上代码所示,咱们能够经过@GlideExtension注解设置本身功能扩展类,使用@GlideOption注解标注对赢扩展功能静态方法,重构项目后Glide注解处理器则会自动在GlideOptions对象和GlideRequest对象中生成相应的方法可以被咱们调用
//调用咱们刚刚设置的扩展功能方法
GlideApp.with(this).load(IMAGE_URL)
                .normalPlaceholder()
                .into(mImageView);
复制代码

GlideType

  • GlideType注解是用于扩展RequestManager的,同理扩展的方法第一个参数必须是RequestManager,并设置类型为加载资源类型,该注解主要做用就是扩展Glide支持加载资源的类型,如下举出官方文档支持gif的一个例子,仍是在咱们刚刚扩展功能类中。
@GlideExtension
public class MyGlideExtension {

    private static final RequestOptions DECODE_TYPE_GIF = decodeTypeOf(GifDrawable.class).lock();

    @GlideType(GifDrawable.class)
    public static void asMyGif(RequestBuilder<GifDrawable> requestBuilder) {
        requestBuilder
                .transition(new DrawableTransitionOptions())
                .apply(DECODE_TYPE_GIF);
    }
}

/**GlideRequests类中生成的asMyGif方法**/

/**
   * @see MyGlideExtension#asMyGif(RequestBuilder)
   */
  @NonNull
  @CheckResult
  public GlideRequest<GifDrawable> asMyGif() {
    GlideRequest<GifDrawable> requestBuilder = this.as(GifDrawable.class);
    MyGlideExtension.asMyGif(requestBuilder);
    return requestBuilder;
  }
复制代码
  • 同理在咱们加载Gif资源的时候能够直接使用
GlideApp.with(this).asMyGif().load(IMAGE_URL)
                .into(mImageView);
复制代码

源码阅读方法思路

  • 看了这么多源码,其实我想说说框架源码阅读的方法思路:
    • 1.首先本身能把框架大致的流程走一遍,而后根据本身刚刚的思路把文章写出来,在写文章的同时也能发现本身刚刚的思路是否有问题,慢慢纠正
    • 2.文章写完,把总体流程图画出来,画图的过程一个是复习思路,还可让本身对源码逻辑更加清晰
    • 3.阅读框架源码时看到英文注释能够先理解其含义,在你看源码没头绪的时候每每思路就在注释中,若是对源码中一个类很迷惑,能够直接看该类的头部注释每每注明了该类的做用。

最后说点

  • 到此,真的很想大叫一声宣泄一下,Glide源码就像一座山,一座高峰,你必须沉住气,慢慢的解读,要否则稍不留神就会掉入代码的海洋,迷失方向。回头看看,你不得不感叹正式因为Glide源码中成千上万行的代码,才造就了这样一个强大的框架。最后,也很是感谢您阅读个人文章,文章中若是有错误,请你们给我提出来,你们一块儿学习进步,若是以为个人文章给予你帮助,也请给我一个喜欢和关注,同时也欢迎访问个人我的博客缓存

  • 参考连接bash

相关文章
相关标签/搜索