Glide源码分析之缓存机制

  2017年9月份,记录了Glide4.0的集成和使用注意事项,后续一直没有对Glide的源码进行深入学习,接下来将对Glide进行逐步深入学习,首先从Glide的缓存机制开始。
  我们都知道,Glide缓存分为内存缓存和磁盘缓存,内存缓存的主要作用是防止应用重复将图片数据读取到内存当中,而硬盘缓存的主要作用是防止应用重复从网络或其他地方重复下载和读取数据。通过这样的缓存机制,不仅提升了图片加载速度,还减少了不必要的流量消耗电量消耗等。那么缓存机制是怎样的呢?下面就开始对缓存流程进行分析。
  注:以下分析的前提条件是允许内存缓存和磁盘缓存。

内存缓存

  这里不对Glide执行流程从头开始分析,直接切入主题,从Glide缓存管理类Engine的load方法开始。

1、缓存Key

  Glide缓存是以Key-Value形式保存。从load方法一开始,通过EngineKeyFactory的bulid方法生成EngineKey对象,这个EngineKey就是Glide中的内存缓存Key,为了保证Key的唯一性,传入了8个参数进行构建EngineKey,并且重写了equals和hashCode方法。最后通过Key从内存缓存中获取目标缓存资源EngineResource(真正的资源是被封装到EngineResource中)对象。

//EngineKey.java
class EngineKey implements Key {
 /**
    * 生成key
    * model:AppGlide.with(context).load(model).into(imageView) ,请求加载的 model(File, Url, Url)
    * signature:签名,可选
    * width, heigh:想要的目标资源大小,override(width, heigh)
    * transformations:资源变换,可选
    * resourceClass:修改过的资源类型Class,eg:Bitmap.class  GifDrawable.class
    * transcodeClass:指定加载的图片格式,eg:asBitmap(),加载的图片资源格式为Bitmap.classs
    * options:额外添加的任何选项
    */
  EngineKey(Object model,Key signature,int width,int height,
      Map<Class<?>, Transformation<?>> transformations, Class<?> resourceClass,
      Class<?> transcodeClass, Options options) {
    ......
  }
}

//Engine.java load方法
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);
2、读取缓存

  Glide内存缓存策略采用二级缓存,分别是ActiveResources和MemoryCache。
  ActiveResources存储的是当前正在使用的资源,内部是通过Map<Key, ResourceWeakReference>来保存弱引用资源,资源在保存到HashMap之前会被ResourceWeakReference引用。

//ActiveResources.java
final class ActiveResources {
   ......
  @VisibleForTesting
  final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();

  /**
  * 存储当前正在显示的资源
  */
  void activate(Key key, EngineResource<?> resource) {
   //将资源保存在弱引用中
    ResourceWeakReference toPut =
        new ResourceWeakReference(
            key,
            resource,
            getReferenceQueue(),
            isActiveResourceRetentionAllowed);
    //将若弱引用资源保存到HashMap中
    ResourceWeakReference removed = activeEngineResources.put(key, toPut);
    if (removed != null) {
      removed.reset();
    }
  }
  ......
}

  MemoryCache的实现类是LruResourceCache,主要实现是采用了LRU算法,存储不使用中的图片资源(即图片资源EngineResource中的引用数acquired=0)。

//GlideBuilder.java
public final class GlideBuilder {
  private MemoryCache memoryCache;
  if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }
  ......
}
//------------------------------------------------------------------------
//LruResourceCache.java
public class LruResourceCache extends LruCache<Key, Resource<?>> implements MemoryCache {
  ......
}
//------------------------------------------------------------------------
//LruCache.java
public class LruCache<T, Y> {
  private final Map<T, Y> cache = new LinkedHashMap<>(100, 0.75f, true);
  ......
}

从源码中可以了解读取内存缓存的大致流程:
(1)通过Key从ActiveResources寻找目标资源。
(2)若(1)中未找到目标资源,则通过Key从MemoryCache中寻找目标资源。
(3)若(1)或者(2)中找到目标资源,则通过ResourceCallback的onResourceReady方法将获取目标资源和缓存来源回调给SingleRequest类(SingleRequest中实现了ResourceCallback接口),在SingleRequest的onResourceReady方法中将目标资源传递给我们设置的RequestListener,若没有RequestListener实例,则传递给封装了ImageView的Target(若into()方法中传入的是ImageVIew,Glide内部通过ImageViewTargetFactory创建BitmapImageViewTarget或DrawableImageViewTarget),将目标资源设置到ImageView中。

//Engine.java
public class Engine implements EngineJobListener,
    MemoryCache.ResourceRemovedListener,
    EngineResource.ResourceListener {
  ......
  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){

      EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);

      //通过Key从ActiveResources寻找目标资源。
      EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
      if (active != null) {
        //通过ResourceCallback的onResourceReady方法将获取目标资源和缓存来源回调给SingleRequest类
        cb.onResourceReady(active, DataSource.MEMORY_CACHE);
        if (VERBOSE_IS_LOGGABLE) {
          logWithTimeAndKey("Loaded resource from active resources", startTime, key);
        }
        return null;
      }
      //通过Key从MemoryCache中寻找目标资源
      EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
      if (cached != null) {
        //通过ResourceCallback的onResourceReady方法将获取目标资源和缓存来源回调给
        cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
        if (VERBOSE_IS_LOGGABLE) {
          logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return null;
    }
  ......
}

  @Nullable
  private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {
      return null;
    }
    EngineResource<?> active = activeResources.get(key);
    if (active != null) {
    //目标资源引用数acquired加1
      active.acquire();
    }
    return active;
  }

  private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {
      return null;
    }

    //从MemoryCache查找到目标资源,删除MemoryCache集合中的目标资源引用
    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
      //目标资源引用数acquired加1
      cached.acquire();
    //从缓存中获取资源显示的同时,将资源添加到activeResources
      activeResources.activate(key, cached);
    }
    return cached;
  }

  private EngineResource<?> getEngineResourceFromCache(Key key) {
    //删除MemoryCache集合中的目标资源
    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;
  }

  ......
}

//SingleRequest.java
public final class SingleRequest<R> implements Request,
    SizeReadyCallback,
    ResourceCallback,
    FactoryPools.Poolable {
  ......

  /**
   * A callback method that should never be invoked directly.
   */
  @SuppressWarnings("unchecked")
  @Override
  public void onResourceReady(Resource<?> resource, DataSource dataSource) {
     ......
    onResourceReady((Resource<R>) resource, (R) received, dataSource);
  }

private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;

    if (glideContext.getLogLevel() <= Log.DEBUG) {
      Log.d(GLIDE_TAG, "Finished loading " + result.getClass().getSimpleName() + " from "
          + dataSource + " for " + model + " with size [" + width + "x" + height + "] in "
          + LogTime.getElapsedMillis(startTime) + " ms");
    }

    isCallingCallbacks = true;
    try {
      boolean anyListenerHandledUpdatingTarget = false;
      //将资源回调给我们实现的RequestListener的onResourceReady方法
      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);

      //若存在RequestListener接口实例,则不会将资源回调给Target(默认target:BitmapImageViewTarget和DrawableImageViewTarget,由ImageViewTargetFactory创建)
      if (!anyListenerHandledUpdatingTarget) {
        Transition<? super R> animation =
            animationFactory.build(dataSource, isFirstResource);
        target.onResourceReady(result, animation);
      }
    } finally {
      isCallingCallbacks = false;
    }

    notifyLoadSuccess();
  }
  ......
}
3、写入缓存

  还记得我们在MemoryCache介绍中提到的EngineResource中的图片资源引用数acquired吗?当资源被使用时(正在使用的资源保存到ActiveResources中),会调用acquire,将变量值+1,当资源被释放时,会调用release()方法,直到acquired=0,表示资源没有被使用,这时候通过会调用onResourceReleased方法,将资源存储到MemoryCache中。

//EngineResource.java
class EngineResource<Z> implements Resource<Z> {
  private int acquired;
  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;
  }

  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) {
      //ResourceListener接口由Engine实现
      listener.onResourceReleased(key, this);
    }
  }
  ......
}
//------------------------------------------------------------------------
//Engine.java
public class Engine implements EngineJobListener,
    MemoryCache.ResourceRemovedListener,
    EngineResource.ResourceListener {

    @Override
    public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
      Util.assertMainThread();
      activeResources.deactivate(cacheKey);
      if (resource.isCacheable()) {
        //将不使用的图片资源缓存到MemoryCache中
        cache.put(cacheKey, resource);
      } else {
        resourceRecycler.recycle(resource);
      }
   }
  ......
}

磁盘缓存

1、缓存Key

(1)未修改过的本地缓存资源Key#ResourceCacheKey

//ResourceCacheKey.java
final class ResourceCacheKey implements Key{
  ResourceCacheKey(ArrayPool arrayPool,Key sourceKey,Key signature,int width,int height,
      Transformation<?> appliedTransformation,Class<?> decodedResourceClass,Options options) {
    ......
  }
}

//ResourceCacheGenerator.java  startNext()
currentKey = new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
              helper.getArrayPool(),
              sourceId,
              helper.getSignature(),
              helper.getWidth(),
              helper.getHeight(),
              transformation,
              resourceClass,
              helper.getOptions());

(2)修改过的本地资源缓存Key#DataCacheKey

//DataCacheKey.java
final class DataCacheKey implements Key{
  DataCacheKey(Key sourceKey, Key signature) {
    this.sourceKey = sourceKey;
    this.signature = signature;
  }
}
//DataCacheGenerator.java  startNext()
 Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
2、读取缓存

  当在内存缓存中获取不到目标图片资源时,在Engine的load方法中,会继续创建任务EngineJob和DecodeJob来从磁盘缓存中查找目标资源,若磁盘中也找不到,则会从网络下载资源,下面我们进行具体分析。
  在Engine的load方法中通过调用EngineJob的start方法启动任务,进入start方法中,可以看到真正去执行任务的是DecodeJob(DecodeJob实现了Runnable接口),通过我们选择的缓存策略,来选择我们将要提交任务的线程池。

//Engine.java load方法
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      current.addCallback(cb);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }
    //创建任务
    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);

    //ResourceCallback接口由SingleRequest实现
    engineJob.addCallback(cb);
    //开启任务
    engineJob.start(decodeJob);

  通过磁盘缓存策略,选择线程池,若缓存策略是DiskCacheStrategy.ALL / DiskCacheStrategy.RESOURCE / DiskCacheStrategy.AUTOMATIC / DiskCacheStrategy.DATA,则允许从修改或者未修改的磁盘缓存资源中获取,选择diskCacheExecutor线程池来执行任务。

//EngineJob.java
 /**
   * Returns true if this job will attempt to decode a resource from the disk cache, and false if it
   * will always decode from source.
   */
 public void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    //是否从磁盘缓存中解码资源
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    executor.execute(decodeJob);
  }
//------------------------------------------------------------------------
//DecodeJob.java
/**
   * Returns true if this job will attempt to decode a resource from the disk cache, and false if it
   * will always decode from source.
   */
  boolean willDecodeFromCache() {
    Stage firstStage = getNextStage(Stage.INITIALIZE);
    //当firstStage是修改或者未修改的磁盘缓存资源,返回true
    return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
  }

private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        //所选的缓存策略是否允许从修改过的磁盘缓存资源中解码资源,若是,则返回RESOURCE_CACHE,反之,继续调用getNextStage方法递归
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        //所选的缓存策略是否允许从未修改的原始磁盘缓存资源中解码资源,若是,则返回Stage.DATA_CACHE,反之,继续调用getNextStage方法递归
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        // Skip loading from source if the user opted to only retrieve the resource from cache.
        //若只从磁盘缓存中获取资源,若此时缓存中没有目标资源,也不会从网络进行加载资源
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }
//------------------------------------------------------------------------
//DiskCacheStrategy.java
DiskCacheStrategy类种各缓存策略中对decodeCachedResource和decodeCachedData的实现这里就不贴出来,可以自行查看。
boolean decodeCachedResource() 返回true的策略有ALL、RESOURCE、AUTOMATIC
boolean decodeCachedData() 返回true的策略有AUTOMATIC、ALL、DATA

  DecodeJob提交到diskCacheExecutor线程池中后,线程池为DecodeJob分配线程执行任务,DecodeJob实现了Runnable接口,所以最终调用的是DecodeJob的run方法。接着调用runWrapped方法,方法中的runReason变量在DecodeJob的构造中赋了初始值INITIALIZE。然后,调用getNextStage方法,来获取磁盘的加载策略Stage。最后,在getNextGenerator方法中通过磁盘加载策略来创建对应加载资源的生成器对象,调用runGenerators方法加载资源。

//DecodeJob.java
private void runWrapped() {
    switch (runReason) {
      case INITIALIZE:
        stage = getNextStage(Stage.INITIALIZE);
        currentGenerator = getNextGenerator();
        //加载资源
        runGenerators();
        break;
      case SWITCH_TO_SOURCE_SERVICE:
        //加载资源
        runGenerators();
        break;
      case DECODE_DATA:
        //处理已经load到的数据
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized run reason: " + runReason);
    }
  }

  磁盘的加载策略Stage,由我们在RequestOptions中设置的缓存策略决定。runReason 初始默认值是INITIALIZE,所以这里current最开始传入的是Stage.INITIALIZE,diskCacheStrategy这个值是由RequestOptions传入,默认缓存策略是DiskCacheStrategy.AUTOMATIC。

  若此时的缓存策略是DiskCacheStrategy.ALL或 DiskCacheStrategy.RESOURCE或DiskCacheStrategy.AUTOMATIC,diskCacheStrategy.decodeCachedResource()将返回true,成员变量stage赋值为Stage.RESOURCE_CACHE。

  若diskCacheStrategy.decodeCachedResource()返回false,继续调用getNextStage方法,current传入Stage.RESOURCE_CACHE。若缓存策略是DiskCacheStrategy.AUTOMATIC或DiskCacheStrategy.ALL或DiskCacheStrategy.DATA,diskCacheStrategy.decodeCachedData()将返回true,成员变量stage赋值为Stage. DATA_CACHE。

  若diskCacheStrategy.decodeCachedData()返回false,继续调用getNextStage方法,current传入Stage. DATA_CACHE,若此时设置了只从磁盘缓存中获取资源,成员变量stage赋值为Stage.FINISHED,反之成员变量stage赋值为Stage. SOURCE。

  磁盘加载数据的策略有三种RESOURCE_CACHE,DATA_CACHE,SOURCE。
  RESOURCE_CACHE:从修改过的本地资源缓存中获取数据。
  DATA_CACHE:从未修改过的本地缓存中获取数据。
  SOURCE:从原始的资源中获取,可能是服务器,也可能是本地的一些原始资源。

//DecodeJob.java
  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:
        // Skip loading from source if the user opted to only retrieve the resource from cache.
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }

  获取了磁盘加载数据的策略后,根据磁盘加载数据策略创建对应的加载资源的生成器。

//DecodeJob.java
  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);
    }
  }

  获得生成器对象后,调用runGenerators方法执行生成器,主要是调用生成器的startNext方法,当startNext启动了DataFetcher加载数据的方法loadData时,返回true。从while的判断条件中可知,若任务被取消或者数据加载成功启动(isCancelled和isStarted其中之一为true),整个while循环就会结束,再接下来的判断中,stage状态为完成或者任务被取消并且数据加载没有启动,则直接notifyFailed,此次加载失败。若while条件判断中任务未被取消(isCancelled为true)且加载数据未成功启动,则执行while循环体中的逻辑。

  执行循环体,若当前的生成器加载数据未成功启动,则通过getNextStage和getNextGenerator获取下一个磁盘加载策略和生成器,直到启动成功或者任务取消,循环结束。简单来说,会依次从ResourceCacheGenerator->DataCacheGenerator->SourceGenerator这样一个链执行,只要其中一个的startNext方法返回为true,则不再寻找下一个Generator。

  例如,第一次循环currentGenerator为ResourceCacheGenerator,假设未启动成功,再次进入getNextStage,当前stage为Stage.RESOURCE_CACHE,所以获取的下一个stage是Stage.DATA_CACHE,通过stage获取下一个生成器DataCacheGenerator。若第二次循环还未启动成功,并且onlyRetrieveFromCache是false,返回下一个stage为Stage.SOURCE,下一个生成器是SourceGenerator,当stage是Stage.SOURCE,内部循环被return,请求reschedule方法重新调度,调度的最终也还是调用runGenerators()。

//DecodeJob.java
  private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    while (!isCancelled && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();

      if (stage == Stage.SOURCE) {
        reschedule();
        return;
      }
    }
    // We've run out of stages and generators, give up.
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
      notifyFailed();
    }

    // Otherwise a generator started a new load and we expect to be called back in
    // onDataFetcherReady.
  }

  获取缓存数据,调用对应DataFetcherGenerator的startNext方法。
  (1)ResourceCacheGenerator#startNext
  首先,通过缓存key获取对应缓存文件cacheFile和对应的加载器集合modelLoaders(cacheFile是File类型,在Glide.java中注册的加载File类型的加载器有FileLoader和ByteBufferFileLoader)。接着,遍历加载器,通过加载器去构建LoadData对象,LoadData内部会创建一个DataFetcher对象,以FileLoader加载器为例,LoadData内部会创建一个FileFetcher对象。然后,通过调用FileFetcher的loadData方法,从缓存中加载数据。最后,将获取的数据通过层层往上回调,显示在我们的目标ImageView上。

  将获取的缓存数据层层回调顺序:FileLoader.java#loadData()—>ResourceCacheGenerator.java#onDataReady()—>DecodeJob.java#onDataFetcherReady() —> EngineJob.java#onResourceReady() ,通过handler切到主线程进行处理—>SingleRequest.java#onResourceReady()—>Target.java#onResourceReady()—>ImageView

1)FileLoader.java#loadData()
  获取的缓存数据通过DataFetcher.DataCallback的onDataReady()方法向ResourceCacheGenerator传递(ResourceCacheGenerator实现了DataFetcher.DataCallback接口)。

2)ResourceCacheGenerator.java#onDataReady()
  缓存数据通过DataFetcherGenerator.FetcherReadyCallback的onDataFetcherReady()方法向DecodeJob传递(DecodeJob实现了DataFetcherGenerator.FetcherReadyCallback接口)

3)DecodeJob.java#onDataFetcherReady()
  首先,调用decodeFromRetrievedData()对获取的数据进行处理(解码/变换等操作)。然后,将处理好的数据传递到notifyComplete()方法。最后,在notifyComplete()方法中调用DecodeJob.Callback的onResourceReady()方法,将数据传递到EngineJob(EngineJob实现了DecodeJob.Callback接口)。

4)EngineJob.java#onResourceReady()
  首先,通过Handler将获取的缓存数据传递到主线程中进行处理。然后,调用handleResultOnMainThread()方法,将从缓存获取的数据封装到EngineResource中。最后,将封装好的EngineResource通过ResourceCallback的onResourceReady()方法传递到SingleRequest(SingleRequest实现了ResourceCallback接口)。

5)SingleRequest.java#onResourceReady()
GlideApp.with(context).load(url).diskCacheStrategy(DiskCacheStrategy.ALL).listener(new RequestListener<Drawable>() {...}).into(imageView/Target);
  还记得上面我们加载图片时设置的listener和into吗?在onResourceReady中是这样处理的,若存在RequestListener接口实例,则不会将获取到的资源回调给Target,反之 会传递给Target。into中若我们设置ImageView,源码中会根据我们选择加载资源的类型(例如:asDrawable()、asBitmap()),为我们创建默认的Target(BitmapImageViewTarget或DrawableImageViewTarget,由ImageViewTargetFactory创建),无论是哪种Target,最终都会将获取到的资源传递给Target的onResourceReady()方法。

6)Target.java#onResourceReady()
  以DrawableImageViewTarget为例,首先,获取的资源传递到了它的父类ImageViewTarget中onResourceReady()方法中,然后,调用setResourceInternal()方法,在方法中通过调用setResource方法设置资源,这个方法在父类中是一个抽象方法,那么,具体实现在DrawableImageViewTarget类中,最后,在具体实现的setResource方法中将获取的图片资源设置到ImageVew中。

//ResourceCacheGenerator.java

@Override
  public boolean startNext() {
    List<Key> sourceIds = helper.getCacheKeys();
    if (sourceIds.isEmpty()) {
      return false;
    }
    List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
    if (resourceClasses.isEmpty()) {
      if (File.class.equals(helper.getTranscodeClass())) {
        return false;
      }
      throw new IllegalStateException(
         "Failed to find any load path from " + helper.getModelClass() + " to "
             + helper.getTranscodeClass());
    }
    while (modelLoaders == null || !hasNextModelLoader()) {
      resourceClassIndex++;
      if (resourceClassIndex >= resourceClasses.size()) {
        sourceIdIndex++;
        if (sourceIdIndex >= sourceIds.size()) {
          return false;
        }
        resourceClassIndex = 0;
      }

      Key sourceId = sourceIds.get(sourceIdIndex);
      Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
      Transformation<?> transformation = helper.getTransformation(resourceClass);
      // PMD.AvoidInstantiatingObjectsInLoops Each iteration is comparatively expensive anyway,
      // we only run until the first one succeeds, the loop runs for only a limited
      // number of iterations on the order of 10-20 in the worst case.
      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;
  }
//FileLoader.java

  @Override
  public LoadData<Data> buildLoadData(@NonNull File model, int width, int height,
      @NonNull Options options) {
    return new LoadData<>(new ObjectKey(model), new FileFetcher<>(model, fileOpener));
  }

  @Override
  public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
     try {
       data = opener.open(file);
     } catch (FileNotFoundException e) {
       if (Log.isLoggable(TAG, Log.DEBUG)) {
         Log.d(TAG, "Failed to open file", e);
       }
       callback.onLoadFailed(e);
       return;
     }
     callback.onDataReady(data);
  }

  (2)DataCacheGenerator#startNext
  与 ResourceCacheGenerator的流程差不多,区别在与它们的缓存key不一样,前者是ResourceCacheKey,后者是DataCacheKey,它们构造key所需的参数不同,具体请看源码,这里就不介绍了。

//DataCacheGenerator.java
@Override
  public boolean startNext() {
    while (modelLoaders == null || !hasNextModelLoader()) {
      sourceIdIndex++;
      if (sourceIdIndex >= cacheKeys.size()) {
        return false;
      }

      Key sourceId = cacheKeys.get(sourceIdIndex);
      // PMD.AvoidInstantiatingObjectsInLoops The loop iterates a limited number of times
      // and the actions it performs are much more expensive than a single allocation.
      @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
      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;
  }

  (3)SourceGenerator#startNext
   dataToCache初始是null,所以,我们先看While里面的流程。磁盘中不存在缓存,所以这是时候的model是GlideUrl,通过GlideUrl获取到加载器类型是HttpGlideUrlLoader,LoadData中对应的DataFetcher是HttpUrlFetcher。调用HttpUrlFetcher的loadData方法,从网络上或者其他地方下载资源,然后将结果通过DataFetcher.DataCallback的onDataReady()方法向SourceGenerator传递(SourceGenerator实现了DataFetcher.DataCallback接口)。在SourceGenerator的onDataReady()中判断数据来源,若数据不是来自远程,则和之前所说的将数据层层往上回调传递流程是一样的;若来自远程,则将数据赋值给dataToCache并且重新调度,最终会再次调用SourceGenerator#startNext,这时dataToCache不等于null了。
   dataToCache不等于null时,调用cacheData()方法,将数据编码作为未修改的资源存储到磁盘中,然后,创建DataCacheGenerator生成器,最后,按照DataCacheGenerator#startNext流程执行。

//SourceGenerator.java
  @Override
  public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    sourceCacheGenerator = null;

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

  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);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Finished encoding source to cache"
            + ", key: " + originalKey
            + ", data: " + dataToCache
            + ", encoder: " + encoder
            + ", duration: " + LogTime.getElapsedMillis(startTime));
      }
    } finally {
      loadData.fetcher.cleanup();
    }

    sourceCacheGenerator =
        new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
  }
3、写入缓存

  上面我们有提到了无磁盘缓存数据时,通过SourceGenerator获取网络或者其他地方的数据,在SourceGenerator的dataToCache不为null时,会调用cacheData方法将资源作为未修改过的资源缓存到磁盘中。
  还有一处写入磁盘缓存的地方,那就我们在层层往上回调数据时,传到DecodeJob,对获取的资源数据进行处理后,在notifyEncodeAndRelease()方法中会将经过解码变换好的资源编码存储到磁盘缓存中。

//DecodeJob.java
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    if (resource instanceof Initializable) {
      ((Initializable) resource).initialize();
    }

    Resource<R> result = resource;
    LockedResource<R> lockedResource = null;
    if (deferredEncodeManager.hasResourceToEncode()) {
      lockedResource = LockedResource.obtain(resource);
      result = lockedResource;
    }

    notifyComplete(result, dataSource);

    stage = Stage.ENCODE;
    try {
      //是否存在编码器
      //Glide.java中默认注册的编码器有ByteBufferEncoder、StreamEncoder、BitmapEncoder、BitmapDrawableEncoder、GifDrawableEncoder
      if (deferredEncodeManager.hasResourceToEncode()) {
        //将处理好的资源编码存储到磁盘缓存中
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      if (lockedResource != null) {
        lockedResource.unlock();
      }
    }
    // Call onEncodeComplete outside the finally block so that it's not called if the encode process
    // throws.
    onEncodeComplete();
  }

  /**
  * 资源解码变换
  */
  @Synthetic
  @NonNull
  <Z> Resource<Z> onResourceDecoded(DataSource dataSource,
      @NonNull Resource<Z> decoded) {
    @SuppressWarnings("unchecked")
    Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
    Transformation<Z> appliedTransformation = null;
    Resource<Z> transformed = decoded;
    if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
      //图片资源变换
      appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
      transformed = appliedTransformation.transform(glideContext, decoded, width, height);
    }
    // TODO: Make this the responsibility of the Transformation.
    if (!decoded.equals(transformed)) {
      decoded.recycle();
    }

    final EncodeStrategy encodeStrategy;
    final ResourceEncoder<Z> encoder;
    //Glide.java中默认注册的编码器:Encoder:ByteBufferEncoder、StreamEncoder、BitmapEncoder、BitmapDrawableEncoder、GifDrawableEncoder
    if (decodeHelper.isResourceEncoderAvailable(transformed)) {
      encoder = decodeHelper.getResultEncoder(transformed);
      encodeStrategy = encoder.getEncodeStrategy(options);
    } else {
      encoder = null;
      encodeStrategy = EncodeStrategy.NONE;
    }

    Resource<Z> result = transformed;
    boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
    if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
        encodeStrategy)) {
      //需要缓存变换后的资源
      if (encoder == null) {
        throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
      }
      final Key key;
      switch (encodeStrategy) {
        case SOURCE:
          key = new DataCacheKey(currentSourceKey, signature);
          break;
        case TRANSFORMED:
          key =
              new ResourceCacheKey(
                  decodeHelper.getArrayPool(),
                  currentSourceKey,
                  signature,
                  width,
                  height,
                  appliedTransformation,
                  resourceSubClass,
                  options);
          break;
        default:
          throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
      }

      LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
      //这里的encoder将赋值给toEncode,toEncode!=null时hasResourceToEncode()返回true
      deferredEncodeManager.init(key, encoder, lockedResult);
      result = lockedResult;
    }
    return result;
  }

参考资料:
https://www.jianshu.com/p/133adedd8860
https://www.jianshu.com/p/e01f68802604

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容