Glide4.9.0 SourceGenerator 源码解析

一、作用

/**
 * Generates {@link com.bumptech.glide.load.data.DataFetcher DataFetchers} from original source data
 * using registered {@link com.bumptech.glide.load.model.ModelLoader ModelLoaders} and the model
 * provided for the load.
 *
 * <p> Depending on the disk cache strategy, source data may first be written to disk and then
 * loaded from the cache file rather than returned directly. </p>
 */

根据官方注释,我们可以看到SourceGenerator用于从用户提供的路径model,获取原始数据data。然后根据磁盘缓存策略,会先缓存在本地,然后再次读取,而不是直接使用原始数据data。

二、源码解析

Glide4.9.0源码 整体概述(二)可以看到,SourceGenerator 的入口方法是startNext,所以我们可以先从这个方法入手。

//SourceGenerator.java
  SourceGenerator(DecodeHelper<?> helper, FetcherReadyCallback cb) {
    this.helper = helper;
    this.cb = cb;
  }
  @Override
  public boolean startNext() {
    //缓存,第一次执行不会跑到这里。
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }
    //移交给sourceCacheGenerator处理,第一次执行不会跑到这里
    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
      	//这里需要注意,假如缓存策略认为可以缓存data,那么就不需要管后面的loadPath,直接先把data获取。
        //因为缓存之后将会移交给DataCacheGenerator处理,所以可以跳过后面的hasLoadPath判断。
      	//hasLoadPath是根据Registry中已经注册的解码器,转换器判断是否可以完成dataclass->resourceclass->transcodeclass的变换。
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

  private boolean hasNextModelLoader() {
    return loadDataListIndex < helper.getLoadData().size();
  }

第一次执行startNext方法,可以看到我们会跑到一个while循环中执行hasNextModelLoader方法,那么helper.getLoadData()是什么呢?建议先看Glide4.9.0 数据转换设计思路

//DecodeHelper
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;
  }

getLoadData方法中会调用getModelLoaders获取modelLoadersGlide4.9.0 getModelLoaders 源码解析),再使用modelLoader#buildLoadData获取LoadData
在这里插入图片描述
在这里插入图片描述
可以看到LoadData其实是ModelLoader接口的一个内部类,LoadData类中持有一个DataFetcher接口实例,DataFetcher接口定义了真正获取数据的loadData方法。ModelLoader接口主要是使用handles方法,判断是否可以处理这个Model。可以的话就实现了Model->data的转换。

Glide内置的ModelLoader有以下这么多,在调用DataFetcher#loadData之后会回调SourceGenerator#onDataReady或者onLoadFailed
在这里插入图片描述
这里看到了刚开始startNext方法中的dataToCache变量,所以就是在这里把获得的data数据保存,这里的cb其实是DecodeJob

//SourceGenerator .java
@Override
  public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      // We might be being called back on someone else's thread. Before doing anything, we should
      // reschedule to get back onto Glide's thread.
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
          loadData.fetcher.getDataSource(), originalKey);
    }
  }

Decode.java再次回调,Glide4.9.0源码 整体概述(二)中提及了回调顺序,所以这里应该是回调EngineJob

//DecodeJob.java
@Override
  public void reschedule() {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);
  }

只是简单的把DecodeJob再次丢进线程池运行。结合Glide4.9.0源码 整体概述(二)可以知道,这时候将会再次执行SourceGenerator,就会跑到了前文所说第一次执行不会走的逻辑,缓存并且移交给DataCacheGenerator处理。

//EngineJob.java
@Override
  public void reschedule(DecodeJob<?> job) {
    // Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself
    // up.
    getActiveSourceExecutor().execute(job);
  }

可是我们前面并没有看到sourceCacheGenerator 的赋值,其实是在cacheData方法的最后一行。

//SourceGenerator.java
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();
    }
	//值得注意的是,这个情况下DataCacheGenerator的回调并不是DecodeJob而是SourceGenerator 。
    sourceCacheGenerator =
        new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
  }
所以可以看到和官方注释的一样,假如设置了缓存原数据,将会存储到本地之后再次读取本地数据,再进行处理。这里也是我不大理解的地方,为什么要读取本地数据,而不是存储之后直接使用内存数据呢?也有可能是由于SourceGenerator#startNext方法中,在设置了缓存原数据的情况下,判断开始任务的时候,跳过了hasLoadPath的判断。欢迎大家讨论。
发布了22 篇原创文章 · 获赞 6 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/u012591964/article/details/88546445