美文网首页
Glide4.9图片框架源码(四)之获取磁盘缓存和图片请求

Glide4.9图片框架源码(四)之获取磁盘缓存和图片请求

作者: 丿学与友 | 来源:发表于2020-05-31 22:58 被阅读0次

    上一节我们讲了加载图片的流程中的内存缓存的二级缓存,如果还不理解,可以回到上节再回顾一下:

    Glide4.9图片框架源码之into方法后续读取内存缓存

    Engine load()方法

    这一节我们着重将一下图片网络请求以及磁盘缓存的流程,首先我们回到上一节的内存缓存的地方:

     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;
        }
    
        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;
        }
    
        EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
        if (current != null) {
          current.addCallback(cb, callbackExecutor);
          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);
    
        engineJob.addCallback(cb, callbackExecutor);
        engineJob.start(decodeJob);
    
        return new LoadStatus(cb, engineJob);
    

    上一节我们讲到了获取内存缓存,内存缓存又分为ActiveResources和cache,具体细节可以看看上一节。如果是首次请求图片,那么是没有缓存的,所以这里的两个loadFromActiveResources和loadFromCache都为空,那么接下来调用了jobs.get(key, onlyRetrieveFromCache)方法去获取一个EngineJob,这里的EngineJob对象是干嘛的呢,我们跟进去看一下:

     private static final EngineResourceFactory DEFAULT_FACTORY = new EngineResourceFactory();
    
      @SuppressWarnings("WeakerAccess")
      @Synthetic
      final ResourceCallbacksAndExecutors cbs = new ResourceCallbacksAndExecutors();
    
      private final StateVerifier stateVerifier = StateVerifier.newInstance();
      private final Pools.Pool<EngineJob<?>> pool;
      private final EngineResourceFactory engineResourceFactory;
      private final EngineJobListener listener;
      private final GlideExecutor diskCacheExecutor;
      private final GlideExecutor sourceExecutor;
      private final GlideExecutor sourceUnlimitedExecutor;
      private final GlideExecutor animationExecutor;
      private final AtomicInteger pendingCallbacks = new AtomicInteger();
    
      private Key key;
      private boolean isCacheable;
      private boolean useUnlimitedSourceGeneratorPool;
      private boolean useAnimationPool;
      private boolean onlyRetrieveFromCache;
      private Resource<?> resource;
    
      @SuppressWarnings("WeakerAccess")
      @Synthetic
      DataSource dataSource;
    
      private boolean hasResource;
    
      @SuppressWarnings("WeakerAccess")
      @Synthetic
      GlideException exception;
    
      private boolean hasLoadFailed;
    
      @SuppressWarnings("WeakerAccess")
      @Synthetic
      EngineResource<?> engineResource;
    
      private DecodeJob<R> decodeJob;
    
      // Checked primarily on the main thread, but also on other threads in reschedule.
      private volatile boolean isCancelled;
    

    看主要的几个成员变量,EngineJob持有了EngineJob的pool,几个线程池GlideExecutor,resource,dataSource,engineResource还有一个decodeJob,这里的decodeJob则是真正去执行请求和磁盘缓存的,可以看出EngineJob主要是控制decodeJob和管理加载过的engineResource缓存,并且管理加载结果的回调。回到上面的jobs.get方法,jobs就是一个包含了Map<Key, EngineJob<?>>的hashmap , 存储了当前对应的EngineJob,获取从缓存jobs中拿到了加载过的EngineJob或者正在加载的,那么直接返回。否则下面会重新创建一个新的EngineJob和DecodeJob,然后jobs‘将EngineJob加入缓存。EngineJob主要对DecodeJob起到一个调度管理并且回调结果的作用,DecodeJob则主要执行获取磁盘缓存和请求。那么继续看下engineJob.start(decodeJob)方法:

      public synchronized void start(DecodeJob<R> decodeJob) {
        this.decodeJob = decodeJob;
        GlideExecutor executor = decodeJob.willDecodeFromCache()
            ? diskCacheExecutor
            : getActiveSourceExecutor();
        executor.execute(decodeJob);
      }
    

    这里的GlideExecutor进去源码发现他其实是一个线程池,Glide提供了很多个线程池,根据所加载的资源类型不同,线程池也不相同。这里不用去纠结线程池,那么看到了他调用了executor.execute(decodeJob)方法,那么decodeJob应该也是一个实现了线程,我们直接看decodeJob的run方法:

        public void run() {
            GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", this.model);
            DataFetcher localFetcher = this.currentFetcher;
            try {
                if (this.isCancelled) {
                    this.notifyFailed();
                    return;
                }
                this.runWrapped();
            } catch (CallbackException var7) {
                throw var7;
            } catch (Throwable var8) {
                if (Log.isLoggable("DecodeJob", 3)) {
                    Log.d("DecodeJob", "DecodeJob threw unexpectedly, isCancelled: " + this.isCancelled + ", stage: " + this.stage, var8);
                }
                if (this.stage != DecodeJob.Stage.ENCODE) {
                    this.throwables.add(var8);
                    this.notifyFailed();
                }
                if (!this.isCancelled) {
                    throw var8;
                }
                throw var8;
            } finally {
                if (localFetcher != null) {
                    localFetcher.cleanup();
                }
                GlideTrace.endSection();
            }
        }
    

    获取磁盘缓存

    这里重点的代码就是try语句里面的代码,catch和finally其实就是对异常的处理和结果的回调还有资源localFetcher的清理,这里不用管,我们继续看runWrapped方法:

       this.runReason = RunReason.INITIALIZE;
      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);
        }
      }
      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;
          case SOURCE:
          case FINISHED:
            return Stage.FINISHED;
          default:
            throw new IllegalArgumentException("Unrecognized stage: " + current);
        }
      }
      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);
        }
      }
      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;
          }
        }
        if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
          notifyFailed();
        }
      }
    

    runWrapped方法进来,有一个runReason变量,这里默认的初始化值是INITIALIZE,因此我们直接看INITIALIZE的case , 调用的getNextStage方法,那么getNextStage也是INITIALIZE的情况,这里调用了diskCacheStrategy的decodeCachedResource方法,这里的DiskCacheStrategy我们回顾下,Glide默认能设置磁盘缓存的属性,比如是否缓存到磁盘,只缓存原文件数据到磁盘,只缓存变换解码后的resource到磁盘等,这里我们没有设置任何磁盘缓存的属性,所以是默认全部都缓存。所以我们这里的diskCacheStrategy.decodeCachedResource()和diskCacheStrategy.decodeCachedData()的返回值全都是true,因此这里getNextStage返回值是RESOURCE_CACHE 。

    回到runWrapped的INITIALIZE情况,继续调用getNextGenerator,根据stage的值,我们生成的Generator对象是ResourceCacheGenerator,然后返回继续调用runGenerators方法。这里有一个isStarted 的布尔值,其赋值的地方是isStarted = currentGenerator.startNext(),表示currentGenerator拿到了或者开启了请求那么就会跳出循环,或者请求被取消也是一样。那么我们看一下startNext方法,这里当前的Generator是ResourceCacheGenerator,跟进去看一下:

    ResourceCacheGenerator startNext()

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

    这里的Generator有三种,分别是ResourceCacheGenerator 、DataCacheGenerator 、SourceGenerator 。ResourceCacheGenerator内部缓存的是ResourceCache,就是缓存的被加工过的原数据缓存,DataCacheGenerator内部缓存的是DataCache表示缓存的是原文件没有任何的处理,而SourceGenerator则是直接从网络加载数据。所以磁盘缓存和网络请求分别对应了这三个重要的对象。

    我们继续说ResourceCacheGenerator 的startNext方法,可以看到通过decodeHelper获取到的sourceIds和resourceClasses如果都是空的,那么直接返回fasle表示没有ResourceCache缓存,否则就去通过sourceIds和resourceClasses这两个list数据拿到当前对应的sourceId 和resourceClass ,然后获取到当前缓存的key就是currentKey,然后通过helper拿到缓存文件cacheFile文件对象,通过这个文件对象,继续获取到对应的modelLoaders集合。然后在modelLoaders中遍历找到合适的loadData ,然后调用loadData.fetcher.loadData方法去解析ResourceCache缓存的数据。

    ModelLoader 、LoadData 、DataFecther

    这里我们先了解下什么是modelLoader和loadData,来看一下ModelLoader的结构:

    public interface ModelLoader<Model, Data> {
    
      class LoadData<Data> {
        public final Key sourceKey;
        public final List<Key> alternateKeys;
        public final DataFetcher<Data> fetcher;
    
        public LoadData(@NonNull Key sourceKey, @NonNull DataFetcher<Data> fetcher) {
          this(sourceKey, Collections.<Key>emptyList(), fetcher);
        }
    
        public LoadData(@NonNull Key sourceKey, @NonNull List<Key> alternateKeys,
            @NonNull DataFetcher<Data> fetcher) {
          this.sourceKey = Preconditions.checkNotNull(sourceKey);
          this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
          this.fetcher = Preconditions.checkNotNull(fetcher);
        }
      }
    
      @Nullable
      LoadData<Data> buildLoadData(@NonNull Model model, int width, int height,
          @NonNull Options options);
    
      boolean handles(@NonNull Model model);
    }
    

    ModelLoader就是一个接口,他有一个内部类LoadData,而LoadData持有一个final对象DataFetcher,我们后面加载数据网络请求就是DataFetcher去实现,还有一个buildLoadData方法。所以由此看来ModelLoader管办理并且初始化LoadData对象,而LoadData才能生成DataFetcher对象去请求和加载图片数据,所以这三个类是一个嵌套的关联的结构。

    Glide提供的ModelLoader有十几种,例如ByteArrayLoader、FileLoader、HttpUriLoader、StringLoader、ResourceLoader等等,不同的ModelLoader实现不同的load功能,那么ModelLoader是在哪里添加进来的呢,为什么是一个集合,我们回到最初的位置看下Glide的初始化方法:

       .append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
            .append(File.class, InputStream.class, new FileLoader.StreamFactory())
            .append(File.class, File.class, new FileDecoder())
            .append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
            // Compilation with Gradle requires the type to be specified for UnitModelLoader here.
            .append(File.class, File.class, UnitModelLoader.Factory.<File>getInstance())
            /* Models */
            .register(new InputStreamRewinder.Factory(arrayPool))
            .append(int.class, InputStream.class, resourceLoaderStreamFactory)
            .append(
                int.class,
                ParcelFileDescriptor.class,
                resourceLoaderFileDescriptorFactory)
            .append(Integer.class, InputStream.class, resourceLoaderStreamFactory)
            .append(
                Integer.class,
                ParcelFileDescriptor.class,
                resourceLoaderFileDescriptorFactory)
            .append(Integer.class, Uri.class, resourceLoaderUriFactory)
            .append(
                int.class,
                AssetFileDescriptor.class,
                resourceLoaderAssetFileDescriptorFactory)
            .append(
                Integer.class,
                AssetFileDescriptor.class,
                resourceLoaderAssetFileDescriptorFactory)
            .append(int.class, Uri.class, resourceLoaderUriFactory)
            .append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
            .append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
            .append(String.class, InputStream.class, new StringLoader.StreamFactory())
            .append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
            .append(
                String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
            .append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
            .append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
    

    在Glide的构造方法中,有一个Registry 调用了所有的这些append方法,将多个ModelLoader添加到里面,我们看下append方法到哪里:

      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;
      }
      public synchronized <Model, Data> void append(
          @NonNull Class<Model> modelClass,
          @NonNull Class<Data> dataClass,
          @NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {
        multiModelLoaderFactory.append(modelClass, dataClass, factory);
        cache.clear();
      }
      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);
      }
     private final List<Entry<?, ?>> entries = new ArrayList<>();
    

    最终一步步添加到了一个entrieslist中,存储了所有类型的ModelLoader,回到我们的ResourceCacheGenerator这里,modelLoaders 通过helper.getModelLoaders(cacheFile)怎么拿到的呢,跟进去看一下:

    List<ModelLoader<File, ?>> getModelLoaders(File file)
        throws Registry.NoModelLoaderAvailableException {
      return glideContext.getRegistry().getModelLoaders(file);
    }
    @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;
    }
    @NonNull
    synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
      try {
        List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
        for (Entry<?, ?> entry : entries) {
          if (alreadyUsedEntries.contains(entry)) {
            continue;
          }
          if (entry.handles(modelClass)) {
            alreadyUsedEntries.add(entry);
            loaders.add(this.<Model, Object>build(entry));
            alreadyUsedEntries.remove(entry);
          }
        }
        return loaders;
      } catch (Throwable t) {
        alreadyUsedEntries.clear();
        throw t;
      }
    }
    

    中间就省略了部分,最后可以看到Model是从entries中循环取出来的,entries就是Glide初始化的时候添加的各种ModelLoader,,好了ModelLoader理解了,ModelLoader构建了LoadData,LoadData生成了Fetcher , 这里的Fetcher又是什么呢,来看下他的结构:

    public interface DataFetcher<T> {
    
      interface DataCallback<T> {
    
        void onDataReady(@Nullable T data);
    
        void onLoadFailed(@NonNull Exception e);
      }
    
      void loadData(@NonNull Priority priority, @NonNull DataCallback<? super T> callback);
    
      void cleanup();
    
      void cancel();
    
      @NonNull
      Class<T> getDataClass();
    
      @NonNull
      DataSource getDataSource();
    }
    

    DataFetcher有一个DataCallback可以看出是他加载数据的成功和失败的回调,loadData则是加载数据的方法,其他的则是取消加载和清除数据等。DataFetcher的实现类同样有十几种,这里同样也是根据不同是加载需求分类不同的DataFetcher,这里我们主要关注的还是HttpUrlFetcher和FileFetcher,还有其他加载本地资源、加载字节码、加载文件等等不同的DataFetcher。到这里ModelLoader 、LoadData 、DataFecther这三个对象的关系分析结束了,我们继续回到上面的ResourceCacheGenerator,上面的ResourceCacheGenerator的startNext结束了,包括去获取缓存,当然要顾全到后面的流程,第一次请求是没有缓存的,所以这里的startNext返回的false,回到startNext返回值的位置:

      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;
          }
        }
        if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
          notifyFailed();
        }
      }
      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;
          case SOURCE:
          case FINISHED:
            return Stage.FINISHED;
          default:
            throw new IllegalArgumentException("Unrecognized stage: " + current);
        }
      }
    

    所以这里的currentGenerator.startNext()返回值是fasle,即没有开始加载数据,所以循环继续,继续调用getNextStage方法,当stage的值是RESOURCE_CACHE,所以根据他的switch语句,getNextStage返回值是DATA_CACHE ,所以getNextGenerator返回的值是DataCacheGenerator,那么循环继续,currentGenerator.startNext()调用的就是DataCacheGenerator的startNext方法:

    DataCacheGenerator startNext

     @Override
      public boolean startNext() {
        while (modelLoaders == null || !hasNextModelLoader()) {
          sourceIdIndex++;
          if (sourceIdIndex >= cacheKeys.size()) {
            return false;
          }
          Key sourceId = cacheKeys.get(sourceIdIndex);
          @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;
      }
    

    这里其实就是去获取DataCache缓存了,helper.getDiskCache().get(originalKey)获取cacheFile 缓存文件,然后跟ResourceGenerator一样去拿modelLoaders,同样也是拿到对应的DataFetcher去加载数据,这里不多分析,那么第一次是没有缓存的,所以DataCacheGenerator的startNext方法也返回的false 继续回调到runGenerators方法中。继续调用getNextStage(stage)方法,当前的stage值为DATA_CACHE,这里的onlyRetrieveFromCache 默认为false,所以getNextStage返回值是SOURCE,那么回到循环,如果stage ==
    Stage.SOURCE,则调用reschedule方法并结束循环和当前方法,我们看下reschedule:

      @Override
      public void reschedule() {
        runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
        callback.reschedule(this);
      }
      @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);
      }
    

    这里其实重新给runReason赋值为SWITCH_TO_SOURCE_SERVICE,并且重新执行了DecodeJob线程,那么我们再次进去DecodeJob的run方法看看:

      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);
        }
      }
      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;
          }
        }
        if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
          notifyFailed();
        }
      }
    

    SourceGenerator StartNext()

    当前是SWITCH_TO_SOURCE_SERVICE,所以调用runGenerators,又开始了循环,只不过当前的currentGenerator是SourceGenerator,所以我们跟进去看SourceGenerator的StartNext方法:

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

    有一个dataToCache 对象,当然这里的dataToCache 肯定为空,是没有缓存的,所以继续往下看,这里的sourceCacheGenerator就是DataCacheGenerator,所以startNext是false,同样没有缓存,所以直接执行下面的代码,不知道有没有细心的同学,前面我们的ResourceCache和DataCache都是根据CacheFile去获取对应ModelLoader,这里却什么都没有了,直接通过 helper.getLoadData()获取ModelLoader,那么跟进去看一下:

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

    glideContext.getRegistry().getModelLoaders(model),这里的model就是我们传进来的图片地址,那么这里的modelLoader直接根据model、图片的宽高去生成一个LoadData,说明此时是没有缓存数据的而是准备通过model的网络地址去加载,所以回到startNext,这里的loadData.fetcher调用的是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));
          }
        }
      }
    

    一个重要的方法,loadDataWithRedirects,入参是URL,请求头信息,返回值是InputStream。毫无疑问,终于看到了我们期待的网络请求了,再跟进去看下代码:

      private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
          Map<String, String> headers) throws IOException {
        if (redirects >= MAXIMUM_REDIRECTS) {
          throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
        } else {
          try {
            if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
              throw new HttpException("In re-direct loop");
            }
          } catch (URISyntaxException e) {
          }
        }
        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);
        urlConnection.setInstanceFollowRedirects(false);
        urlConnection.connect();
        stream = urlConnection.getInputStream();
        if (isCancelled) {
          return null;
        }
        final int statusCode = urlConnection.getResponseCode();
        if (isHttpOk(statusCode)) {
          return getStreamForSuccessfulRequest(urlConnection);
        } else if (isHttpRedirect(statusCode)) {
          String redirectUrlString = urlConnection.getHeaderField("Location");
          if (TextUtils.isEmpty(redirectUrlString)) {
            throw new HttpException("Received empty or null redirect url");
          }
          URL redirectUrl = new URL(url, redirectUrlString);
          cleanup();
          return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
        } else if (statusCode == INVALID_STATUS_CODE) {
          throw new HttpException(statusCode);
        } else {
          throw new HttpException(urlConnection.getResponseMessage(), statusCode);
        }
      }
    

    看到这里,是不是心里有一点小激动呢,找到了熟悉的感觉,urlConnection,我们看回调成功的方法getStreamForSuccessfulRequest:

      private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
          throws IOException {
        if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
          int contentLength = urlConnection.getContentLength();
          stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
        } else {
          if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
          }
          stream = urlConnection.getInputStream();
        }
        return stream;
      }
    

    成功请求图片完成并且返回了一个InputStream ,到这里整个获取内存缓存,磁盘缓存,请求图片的过程就结束了。

    总结

    当我们的内存缓存,ActiveResource 和 MemoryCache 都没有获取到的时候,这时通过jobs获取缓存的加载过或者正在加载的EngineJob,如果拿到了则立刻返回,没有拿到则去new一个EngineJob和DecodeJob并且添加到jobs缓存,EngineJob用于管理执行DecodeJob并且回调执行的结果,DecodeJob是一个线程,线程执行调用runWrapped方法分别去获取ResourceCache和DataCache,分别表示处理过源数据的缓存和没有处理过的源数据缓存,通过对应的cachekey获取到磁盘缓存文件cacehFile,通过cacehFile找到对应的ModelLoader,然后通过ModelLoader build生成LoadData,然后继续通过LoadData中的DataFetcher去加载缓存的数据源。如果两个磁盘缓存都没拿到,那么则调用SourceGenerator 的startNext方法,通过传入的model图片网络地址生成对应的ModelLoader,再去加载网络请求,得到一个InputStream。到这里整个磁盘缓存和网络请求就结束了,那么获取到图片的资源后,是怎么加载进磁盘缓存和内存缓存,还有显示到View上的呢,下一节我们继续浏览分析源码~

    相关文章

      网友评论

          本文标题:Glide4.9图片框架源码(四)之获取磁盘缓存和图片请求

          本文链接:https://www.haomeiwen.com/subject/ozirzhtx.html