Glide图标加载库-磁盘缓存分析

总述

接着上面文章继续分析磁盘缓存.首先在上一篇文章我们知道了缓存类是在什么地方创建的,那么我就依次为线索,分析其最终在哪里调用了.因为磁盘缓存经过多次参数传递,不太好发现,所以先通过其构建去寻找,这也叫阅读源码中的一个思路吧.

磁盘缓存类分析

GliderBuilder创建

  • diskCacheFactory继承DiskLruCacheFactory
  • 其内部重写了缓存目录创建方法
  • DiskLruCache类相关的缓存原理,网上文章比较多,有兴趣可搜索查看
if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }

传递给Engine

engine =  new Engine(memoryCache,diskCacheFactory,...);

通过LazyDiskCacheProvider包裹

  • 后面创建DiskLruCache的工厂类变成了diskCacheProvider
//Engine的构造方法
this.diskCacheProvider = new LazyDiskCacheProvider(diskCacheFactory);

传递到DecodeJobFactory

  • DecodeJob是负责数据加载处理的核心类
  • DecodeJobFactory是Engine的静态内部类
//Engine的构造方法
if (decodeJobFactory == null) {
      decodeJobFactory = new DecodeJobFactory(diskCacheProvider);
    }

创建DecodeJob时,设置diskCacheProvider

  • DecodeJob根据流程图,其是在Engine.load中创建
  • DecodeJob有DecodeJobFactory.build创建
DecodeJob<R> decodeJob =decodeJobFactory.build();
  • build方法,通过Pools去获取DecodeJob实例
  • 我们从这里可以学到一种构建对象的方法
build(...){
DecodeJob<R> result = Preconditions.checkNotNull((DecodeJob<R>) pool.acquire());
}
final Pools.Pool<DecodeJob<?>> pool =
        FactoryPools.threadSafe(
            JOB_POOL_SIZE,
            new FactoryPools.Factory<DecodeJob<?>>() {
              @Override
              public DecodeJob<?> create() {
                return new DecodeJob<>(diskCacheProvider, pool);
              }
            });

缓存添加方法的调用方法

-上面包裹类LazyDiskCacheProvider为DecodeJob.DiskCacheProvider接口实现类

  • 我们将其传递给给了DecodeJob
  • LazyDiskCacheProvider重写的接口方法会通过GlideBuild传递的diskCacheFactory获得缓存类DiskCache
private static class LazyDiskCacheProvider implements DecodeJob.DiskCacheProvider
interface DiskCacheProvider {
    DiskCache getDiskCache();
  }
  
  public DiskCache getDiskCache() {
      if (diskCache == null) {
        synchronized (this) {
          if (diskCache == null) {
            diskCache = factory.build();
          }
          if (diskCache == null) {
            diskCache = new DiskCacheAdapter();
          }
        }
      }
      return diskCache;
    }
  }
  • 添加缓存方法put的调用
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的encode方法,我们记住这个方法,那它是在何时调用的呢,我们接下啦回到数据的加载过程,开始分析,同时也能找到磁盘缓存的读取.

磁盘缓存的读取

通过上一篇的流程分析,我们知道数据的加载最终有DecodeJob中的三个核心方法处理:getNextStage;getNextGenerator();runGenerators

  • getNextStage,会根据当前不同的状态,前往下一级状态,逐级处理,这就是磁盘缓存策略配置的控制方法,默认是初始化状态,我们默认配置了磁盘缓存,且是DataChache
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;
  • 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);
    }
  }
  • runGenerators,内部会不到循环,调用getNextState,getNextGenerator,知道相应处理类的获取到数据,当最后是Stage.Source时,回到EngineJob,然后再次调用DecodeJob,当状态直接变成了SWITCH_TO_SOURCE_SERVICE,表示没有获取到缓存,调用SourceGenerator.statrNext方法获取数据.
  • 我们这里分析磁盘缓存,就不展开,后面分析写入展开,主要分析DataCacheGenerator,进入其startNext方法;
  • startNext,首先生成相应的key,然后调用LoadData.fetch.loadData
 Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
      cacheFile = helper.getDiskCache().get(originalKey)
      
 cacheFile = helper.getDiskCache().get(originalKey);
 
 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);
      }
  • 在上一篇我们分析了Glide的构造方法,说明其在里面注册了各种类型的loader,其获取过程比较繁琐,我们更加相应的类直接查看,磁盘缓存,显然是FileLoader
  • FileLoader的buildData方法返回FileFetcher,其是静态内部类
@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));
  }
  • FileFecher类的loadData方法,根据得到的cacheFile,获取数据
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);
    }
  • 接下来就是依次回调,与网络请求回调逻辑大致一致,大家可根据流程图查看,最终设置给imageView,后面分析写入也会展开

到这里,磁盘缓存的读取完成,发现其有两种类型磁盘缓存.一种是Resource,一种是Data.具体区别看类的注释.代码注释和命名对可读性帮助很大呀

/**
 * Generates {@link com.bumptech.glide.load.data.DataFetcher DataFetchers} from cache files
 * containing downsampled/transformed resource data.
 */
class ResourceCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object> 
/**
 * Generates {@link com.bumptech.glide.load.data.DataFetcher DataFetchers} from cache files
 * containing original unmodified source data.
 */
 class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object> 

磁盘的写入

假设第一次调用,磁盘缓存没有,通过上面的分析,知道会调用SourceGenerator的startNext方法.在里面查找loadData,然后调用loadData.fetch.loadData

  • 这里loadData是什么,fetch是那个类,我们在上篇文章中Glide的构造方法提及了,下面就逐步分析一下.
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);
      }
    }

Loader的添加和查找

loader的添加
  • Glide的构造方法中通过regeister.append添加
  • 其会传递三个参数,第一个Model.class,第二个数据类型类,第三个工厂类
registry.append(ByteBuffer.class, new ByteBufferEncoder())
....
 .append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
        .append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
        .append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
        .append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
  • regeister.append添加方法调用ModelLoaderRegister.append方法
 @NonNull
  public <Model, Data> Registry append(
      @NonNull Class<Model> modelClass,
      @NonNull Class<Data> dataClass,
      @NonNull ModelLoaderFactory<Model, Data> factory) {
    modelLoaderRegistry.append(modelClass, dataClass, factory);
    return this;
  }
  • 在ModelLoaderRegister.append方法调用MultiModelLoaderFactory的append方法
synchronized <Model, Data> void append(
      @NonNull Class<Model> modelClass,
      @NonNull Class<Data> dataClass,
      @NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {
    add(modelClass, dataClass, factory, /*append=*/ true);
  }
  • MultiModelLoaderFactory的添加方法会将其添加到Entry中
  • ==Entry有三个变量,就是我们在Glide传递进去的参数==
  • 到这里就将添加完成
private <Model, Data> void add(
      @NonNull Class<Model> modelClass,
      @NonNull Class<Data> dataClass,
      @NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory,
      boolean append) {
    Entry<Model, Data> entry = new Entry<>(modelClass, dataClass, factory);
    entries.add(append ? entries.size() : 0, entry);
  }
loader的查找

我们获取网络图片,传递的是字符串类型,数据类型是流

  • sourcegenerator.startNext查找,采取的是遍历的方法,发现不符合条件,会递增,再去获取,我们记住这里的判断条件,后面拆开分析
  • helper.getLoadData()调用register.getModelLoader方法,获取modelLoaders,然后遍历调用每一个loader的buildLoadData方法
 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);
      }
    }
List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear();
      List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
      //noinspection ForLoopReplaceableByForEach to improve perf
      for (int i = 0, size = modelLoaders.size(); i < size; i++) {
        ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
        LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
        if (current != null) {
          loadData.add(current);
        }
      }
    }
    return loadData;
  }
 
  • register的getModelLoaders会调用ModelLoaderRegister.getModelLoaders方法,类型委托模式,register的添加和获取都是通过ModelLoaderRegister
  • ModelLoaderRegister.getModelLoaders内部调用getModelLoadersForClass
@NonNull
  public <Model> List<ModelLoader<Model, ?>> getModelLoaders(@NonNull Model model) {
    List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
    if (result.isEmpty()) {
      throw new NoModelLoaderAvailableException(model);
    }
    return result;
  }
  
   public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
    List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
    ····
    
  }
  • getModelLoadersForClass内部会调用MultiModelLoaderFactory.build方法,里面会传入model类型,其通过DecodeHeelper传入,而DecodeHelper在DecodeJob构造时,创建,传入.那我们知道其就是String.class
  • 在MultiModelLoaderFactory.build方法会调用entry.build方法
  • 我们刚才分析添加时,就是添加进里面了,因为model.class是string.class有多个,接下里我们进入StringLoader类
synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
    try {
      List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
      for (Entry<?, ?> entry : entries) {
        
        if (entry.handles(modelClass)) {
          alreadyUsedEntries.add(entry);
          //这里是重点
          loaders.add(this.<Model, Object>build(entry));
          alreadyUsedEntries.remove(entry);
        }
      }
      return loaders;
    
  }
 private <Model, Data> ModelLoader<Model, Data> build(@NonNull Entry<?, ?> entry) {
    return (ModelLoader<Model, Data>) Preconditions.checkNotNull(entry.factory.build(this));
  }
  • string.class利用有多个Factory,如果是网络请求,显然是StreamFactory,
  • 里面又回调用Glide构造方法添加的modle为Url.class的工厂方法,在构造方法中我们发现了HttpUrlLoader.Factory;那我们看其build方法返回的loader
public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {
public ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
      return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));//里面的方法调用更上面的一样,就不分析,直接分析factory.build方法
    }
    
public StringLoader(ModelLoader<Uri, Data> uriLoader) {
    this.uriLoader = uriLoader;
  }
  
public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
      return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
    }
  
  • HttpUrlLoader返回的里面跟StringLoader一样,又套了一层,GlideUrl.class,这是4.10进行修改了,添加进去的.绕的有点迷糊,我们不再分析,直接查找GlideUrl.class添加是的Factory
 append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
  • 在HttpGlideUrlLoader里我们找到了Fetcher
 @Override
  public LoadData<InputStream> buildLoadData(
      @NonNull GlideUrl model, int width, int height, @NonNull Options options) {
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
  }
  • 在Fecher里面我们发现了网络请求,和SourceGenerator判断方法参数的实现.
public Class<InputStream> getDataClass() {
    return InputStream.class;
  }
  @NonNull
  @Override
  public DataSource getDataSource() {
    return DataSource.REMOTE;
  }
  
   /** Indicates data was retrieved from a remote source other than the device. */
  REMOTE,
  • 显然我们是获取网络数据,其符合条件的
  • 然后调用HttpUrlFetch的loadData方法,内部会调用loadDataWithRedirects,获取网络流
loadData{
 InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
  callback.onDataReady(result);
 }
 private InputStream loadDataWithRedirects(
      URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
    urlConnection = connectionFactory.build(url);
    for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
      urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
    }
    urlConnection.setConnectTimeout(timeout);
    urlConnection.setReadTimeout(timeout);
    urlConnection.setUseCaches(false);
    urlConnection.setDoInput(true)
    }

到这里我们分析了Loader的查找,然后找到了网络请求的处理类,然后就是一次回调,回调流程,可以查看流程图,其中有个步骤,就是回调DecodeJob.onDataFetcherReady.接下来就是查找我们一开始通过磁盘缓存类的传递,最终到了DecodeJob.encode方法,我们看DecodeJob.onDataFetcherReady是如何逐步调用到给方法

磁盘缓存写入方法的调用

  • 网络请求成功返回数据后,会进行解码工作,通过decodeFromRetrievedData实现
  • decodeFromRetrievedData调用decodeFromData完成解码,
  • 然后调用notifyEncodeAndRelease
public void onDataFetcherReady(
      Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
 ...
        decodeFromRetrievedData();
 ...    
  }
  
  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    }
    ...
  }
  • notifyEncodeAndRelease里面又两个方法,一个负责回调,返回EngineJob,一个就是磁盘缓存写入
  • notifyComplete负责回调到EngineJob
  • deferredEncodeManager.encode负责磁盘缓存写入,这里我们发现了diskCacheProvider,options
  • deferredEncodeManager.encode负责添加缓存
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    
    Resource<R> result = resource;
    notifyComplete(result, dataSource);
    stage = Stage.ENCODE;
    try {
      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();
  }
  
  private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
    callback.onResourceReady(resource, dataSource);
  }
  
   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();
      }
    }

到此通过简单调用分析大致流程,绘制流程图,然后分析缓存策略,逐步分析图片加载过程的每一个步骤.通过源码阅读发现,Gldie在每次升级会修改部分逻辑,但大致逻辑没有修改,其核心思想都是一致的.同时也可以观察到其覆盖了多种情况,一个简单的调用里面涉及了如此多的逻辑.设计的非常好.

https://juejin.im/post/5deb3ab6f265da33910a31a9

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论