Glide之SourceGenerator数据请求和磁盘缓存

先看一下对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. 

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.

中文意思就是在加载的时候,使用注册的ModelLoaders和model类型去生成DataFetcher。

其实生成DataFetcher是调用DecodeHelper.getLoadData来完成的。如果满足条件执行DataFectcher的loadData去获取数据。看源码

  @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;
        startNextLoad(loadData);
      }
    }
    return started;
  }

  private void startNextLoad(final LoadData<?> toStart) {
    loadData.fetcher.loadData(
        helper.getPriority(),
        new DataCallback<Object>() {
          @Override
          public void onDataReady(@Nullable Object data) {
            if (isCurrentRequest(toStart)) {
              onDataReadyInternal(toStart, data);
            }
          }

          @Override
          public void onLoadFailed(@NonNull Exception e) {
            if (isCurrentRequest(toStart)) {
              onLoadFailedInternal(toStart, e);
            }
          }
        });
  }

看loadData = helper.getLoadData().get(loadDataListIndex++);这句话就是从LoadData list中获取一个LoadData,然后根据磁盘缓存策略,磁盘缓存策略稍后说,先看主线,采用DataFetcher去请求数据。还是Model以String为例,具体的ModelLoader分析,请看Glide 之 Registry、ModelLoaderRegistry、 MultiModelLoaderFactory、 ModelLoader 分析

根据文章中的分析,我们知道load.fetcher.loadData最终会调用到HttpUrlFetcher.loadData方法

  @Override
  public void loadData(
      @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      callback.onDataReady(result);
    } catch (IOException e) {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "Failed to load data for url", e);
      }
      callback.onLoadFailed(e);
    } finally {
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
      }
    }
  }

HttpUrlFetcher中是通过HttpURLConnection去获取网络数据的,获取到的InputStream result通过callback返回给了SourceGenerator中,就是上面拿到的Object data对象

  void onDataReadyInternal(LoadData<?> loadData, 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);
    }
  }

看cb.reschedule(),cb是在DecodeJob中实例化SourceGenerator时候传入的FetcherReadyCallback回调,DecodeJob实现了FetcherReadyCallback,所以,我们看看DecodeJob中的reschedule方法

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

这个callback是DecodeJob.Callback,EngineJob实现了这个接口,也就是说callback指的就是EngineJob,我们看看EngineJob的reschedule方法

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

这里用到了线程池,看Glide之线程池

这里就是重新执行一遍DecodeJob,实现了Runnable接口。我们看DecodeJob的run方法

先总结一下:上面的流程就是通过HttpUrlFetcher请求回来数据后,先在SourceGenerator中暂存起来dataToCache,然后通过FetcherReadyCallback(DecodeJob实现)->DecodeJob.Callback(EngineJob实现)回调到EngineJob中,重新执行一遍DecodeJob。

public void run() {
    ...
    runWrapped();
    ...
}
  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:
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized run reason: " + runReason);
    }
  }

看上面DecodeJob的reschedule代码,我们知道此时runReason是SWITCH_TO_SOURCE_SERVICE

  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.
  }

此时DecodeJob中的currentGenerator是SourceGenerator,看到这里,又回到了SourceGenerator的startNext方法里面。

那么问题来了,为啥要折腾这一圈,在SourceGenarator直接解决不就完事了吗?注释中给了说明

We might be being called back on someone else's thread. Before doing anything, we should reschedule to get back onto Glide's thread.

我们分析SourceGenarator的startNext的方法的时候,只分析了下半部分,现在看看上半部分:

 public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

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

    ...
  }

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

helper.getSourceEncoder 的分析请看Glide之Registry、EncoderRegistry、Encoder分析

我们先看看helper.getDiskCache()

  DiskCache getDiskCache() {
    return diskCacheProvider.getDiskCache();
  }

我们先研究一下这个DiskCache,看Glide之DiskCache这篇文章

inputStream写入了文件里,然后实例化了一个DataCacheGenerator(这个对象只是SourceGenerator中使用的,和DecodeJob执行过程的的那个没有关系),执行startNext方法。DataCacheGenerator的startNext方法是从磁盘缓存DATA_CACHE中取出存的数据,然后有传给SourceGenerator(实现了FetcherReadyCallback接口),然后又传给了DecodeJob(实现了FetcherReadyCallback接口),在DecodeJob对数据进行decode成要展示的数据,传给EngineJob,在EngineJob中会切换到主线程展示。这时工作线程source-thread-1会依次执行完DataCacheGenerator.startNext、SourceGenerator.startNext、DecodeJob.run方法,退出线程。

Glide源码分析(五),EngineJob与DecodeJob代码详细加载过程

发布了189 篇原创文章 · 获赞 25 · 访问量 22万+

猜你喜欢

转载自blog.csdn.net/lizhongyisailang/article/details/104154763