zoukankan      html  css  js  c++  java
  • Glide缓存流程

    本文首发于 vivo互联网技术 微信公众号 
    链接:https://mp.weixin.qq.com/s/cPLkefpEb3w12-uoiqzTig
    作者:连凌能

    Android上图片加载的解决方案有多种,但是官方认可的是Glide。Glide提供简洁易用的api,整个框架也方便扩展,比如可以替换网络请求库,同时也提供了完备的缓存机制,应用层不需要自己去管理图片的缓存与获取,框架会分成内存缓存,文件缓存和远程缓存。本文不会从简单的使用着手,会把重点放在缓存机制的分析上。

    一、综述

    开始之前,关于Glide缓存请先思考几个问题:

    • Glide有几级缓存?

    • Glide内存缓存之间是什么关系?

    • Glide本地文件IO和网络请求是一个线程吗?如果不是,怎么实现线程切换?

    • Glide网络请求回来后数据直接返回给用户还是先存再返回?

    加载开始入口从Engine.load()开始,先看下对这个方法的注释,

    • 会先检查(Active Resources),如果有就直接返回,Active Resources没有被引用的资源会放入Memory Cache,如果Active Resources没有,会往下走。

    • 检查Memory Cache中是否有需要的资源,如果有就返回,Memory Cache中没有就继续往下走。

    • 检查当前在运行中的job中是否有改资源的下载,有就在现有的job中直接添加callback返回,不重复下载,当然前提是计算得到的key是一致的,如果还是没有,就会构造一个新的job开始新的工作。

    * Starts a load for the given arguments.
    *
    * <p>Must be called on the main thread.
    *
    * <p>The flow for any request is as follows:
    * <ul>
    *   <li>Check the current set of actively used resources, return the active resource if
    *   present, and move any newly inactive resources into the memory cache.</li>
    *   <li>Check the memory cache and provide the cached resource if present.</li>
    *   <li>Check the current set of in progress loads and add the cb to the in progress load if
    *   one is present.</li>
    *   <li>Start a new load.</li>
    * </ul>
    

    ok, find the source code.

    二、内存缓存

    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) {
      Util.assertMainThread();
      long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
     
      EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
          resourceClass, transcodeClass, options);
     
      // focus 1
      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;
      }
      // focus 2
      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;
      }
     
      // focus 3
      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);
     
      engineJob.addCallback(cb);
      // focus 4
      engineJob.start(decodeJob);
     
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Started new load", startTime, key);
      }
      return new LoadStatus(cb, engineJob);
    }
    

    先看到 focus 1,这一步会从 ActiveResources 中加载资源,首先判断是否使用内存缓存,否的话返回null;否则到 ActiveResources 中取数据:

    // Engine.java
     @Nullable
     private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
       if (!isMemoryCacheable) {
         return null;
       }
       EngineResource<?> active = activeResources.get(key);
       if (active != null) {
         active.acquire();
       }
     
       return active;
     }
    

    接下来看下ActiveResources, 其实是用过弱引用保存使用过的资源。

    final class ActiveResources {
     
      ...
      private final Handler mainHandler = new Handler(Looper.getMainLooper(), new Callback() {
        @Override
        public boolean handleMessage(Message msg) {
          if (msg.what == MSG_CLEAN_REF) {
            cleanupActiveReference((ResourceWeakReference) msg.obj);
            return true;
          }
          return false;
        }
      });
      @VisibleForTesting
      final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
         
      ...
    }
    

    成功取到数据后回调类型也是内存缓存:

    
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
       cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
       return null;
    }
    

    接着回到Engine.load()中继续看到focus 2,如果在cache中找到就是remove掉,然后返回EngineResource,其中需要EngineResource进行acquire一下,这个后面再看,然后会把资源移到ActiveResources中,也就是上面提到的缓存:

    // Engine.java
      private final MemoryCache cache;
     
      private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
        if (!isMemoryCacheable) {
          return null;
        }
     
        EngineResource<?> cached = getEngineResourceFromCache(key);
        if (cached != null) {
          cached.acquire();
          activeResources.activate(key, cached);
        }
        return cached;
      }
     
      private EngineResource<?> getEngineResourceFromCache(Key key) {
        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;
      }
    

    其中cache是MemoryCache接口的实现,如果没设置,默认在build的时候是LruResourceCache, 也就是熟悉的LRU Cache:

    // GlideBuilder.java
    if (memoryCache == null) {
       memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }
    

    再看下EngineResource,主要是对资源增加了引用计数的功能:

    // EngineResource.java
    private final boolean isCacheable;
      private final boolean isRecyclable;
      private ResourceListener listener;
      private Key key;
      private int acquired;
      private boolean isRecycled;
      private final Resource<Z> resource;
     
      interface ResourceListener {
        void onResourceReleased(Key key, EngineResource<?> resource);
      }
     
      EngineResource(Resource<Z> toWrap, boolean isCacheable, boolean isRecyclable) {
        resource = Preconditions.checkNotNull(toWrap);
        this.isCacheable = isCacheable;
        this.isRecyclable = isRecyclable;
      }
     
      void setResourceListener(Key key, ResourceListener listener) {
        this.key = key;
        this.listener = listener;
      }
     
      Resource<Z> getResource() {
        return resource;
      }
     
      boolean isCacheable() {
        return isCacheable;
      }
     
      @NonNull
      @Override
      public Class<Z> getResourceClass() {
        return resource.getResourceClass();
      }
     
      @NonNull
      @Override
      public Z get() {
        return resource.get();
      }
     
      @Override
      public int getSize() {
        return resource.getSize();
      }
     
      @Override
      public void recycle() {
        if (acquired > 0) {
          throw new IllegalStateException("Cannot recycle a resource while it is still acquired");
        }
        if (isRecycled) {
          throw new IllegalStateException("Cannot recycle a resource that has already been recycled");
        }
        isRecycled = true;
        if (isRecyclable) {
          resource.recycle();
        }
      }
     
      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) {
          listener.onResourceReleased(key, this);
        }
      }
    

    在release后会判断引用计数是否为0,如果是0就会回调onResourceReleased,在这里就是Engine,然后会把资源从ActiveResources中移除,资源默认是可缓存的,因此会把资源放到LruCache中。

    // Engine.java
      @Override
      public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
        Util.assertMainThread();
        activeResources.deactivate(cacheKey);
        if (resource.isCacheable()) {
          cache.put(cacheKey, resource);
        } else {
          resourceRecycler.recycle(resource);
        }
      }
     
    // ActiveResources.java
      void activate(Key key, EngineResource<?> resource) {
        ResourceWeakReference toPut =
            new ResourceWeakReference(
                key,
                resource,
                getReferenceQueue(),
                isActiveResourceRetentionAllowed);
     
        ResourceWeakReference removed = activeEngineResources.put(key, toPut);
        if (removed != null) {
          removed.reset();
        }
      }
     
      void deactivate(Key key) {
        ResourceWeakReference removed = activeEngineResources.remove(key);
        if (removed != null) {
          removed.reset();
        }
      }
    

    如果是回收呢,看看上面的EngineResource,如果引用计数为0并且还没与回收,就会调用真正的Resource.recycle(),看其中的一个BitmapResource是怎么回收的,就是放到Bitmap池中,也是用的LRU Cache,这个和今天的主题不相关,就不继续往下拓展。

    // BitmapResource.java
     @Override
     public void recycle() {
       bitmapPool.put(bitmap);
     }
    

    思路再拉到Engine.load()的流程中,接下来该看focus 3,这里再贴一下代码,如果job已经在运行了,那么直接添加一个回调后返回LoadStatus,这个可以允许用户取消任务:

    // Engine.java
    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);
    }
     
    // LoadStatus
      public static class LoadStatus {
        private final EngineJob<?> engineJob;
        private final ResourceCallback cb;
     
        LoadStatus(ResourceCallback cb, EngineJob<?> engineJob) {
          this.cb = cb;
          this.engineJob = engineJob;
        }
     
        public void cancel() {
          engineJob.removeCallback(cb);
        }
      }
    

    接着往下看到focus 4, 到这里就需要创建后台任务去拉取磁盘文件或者发起网络请求。

    三、磁盘缓存

    // Engine.java
       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);
        engineJob.start(decodeJob);
        return new LoadStatus(cb, engineJob);
    

    先构造两个job,一个是EngineJob,另外一个DecodeJob,其中DecodeJob会根据需要解码的资源来源分成下面几个阶段:

    // DecodeJob.java
      /**
       * Where we're trying to decode data from.
       */
      private enum Stage {
        /** The initial stage. */
        INITIALIZE,
        /** Decode from a cached resource. */
        RESOURCE_CACHE,
        /** Decode from cached source data. */
        DATA_CACHE,
        /** Decode from retrieved source. */
        SOURCE,
        /** Encoding transformed resources after a successful load. */
        ENCODE,
        /** No more viable stages. */
        FINISHED,
      }
    

    在构造DecodeJob时会把状态置为INITIALIZE。

    构造完两个 Job 后会调用 EngineJob.start(DecodeJob),首先会调用getNextStage来确定下一个阶段,这里面跟DiskCacheStrategy这个传入的磁盘缓存策略有关。

    磁盘策略有下面几种:

    • **ALL: **缓存原始数据和转换后的数据

    • **NONE: **不缓存

    • **DATA: **原始数据,未经过解码或者转换

    • **RESOURCE: **缓存经过解码的数据

    • **AUTOMATIC(默认):**根据`EncodeStrategy`和`DataSource`等条件自动选择合适的缓存方

    默认的AUTOMATIC方式是允许解码缓存的RESOURCE:

    public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
          return dataSource == DataSource.REMOTE;
        }
     
        @Override
        public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,
            EncodeStrategy encodeStrategy) {
          return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
              || dataSource == DataSource.LOCAL)
              && encodeStrategy == EncodeStrategy.TRANSFORMED;
        }
     
        @Override
        public boolean decodeCachedResource() {
          return true;
        }
     
        @Override
        public boolean decodeCachedData() {
          return true;
        }
      };
    

    所以在 getNextStage 会先返回Stage.RESOURCE_CACHE,然后在start中会返回diskCacheExecutor,然后开始执行DecodeJob:

    // EngineJob.java
    public void start(DecodeJob<R> decodeJob) {
        this.decodeJob = decodeJob;
        GlideExecutor executor = decodeJob.willDecodeFromCache()
            ? diskCacheExecutor
            : getActiveSourceExecutor();
        executor.execute(decodeJob);
    }
     
    // DecodeJob.java
      boolean willDecodeFromCache() {
        Stage firstStage = getNextStage(Stage.INITIALIZE);
        return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
      }
     
      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会回调run()开始执行, run()中调用runWrapped执行工作,这里runReason还是RunReason.INITIALIZE ,根据前面的分析指导这里会获得一个ResourceCacheGenerator,然后调用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:
            decodeFromRetrievedData();
            break;
          default:
            throw new IllegalStateException("Unrecognized run reason: " + runReason);
        }
      }
     
      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,目前currentGenerator是ResourceCacheGenerator, 那么就是调用它的startNext方法:

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

    看下ResourceCacheGenerator.startNext(), 这里面就是重点逻辑了,首先从Registry中获取支持资源类型的ModelLoader(其中ModelLoader是在构造Glide的时候传进去), 然后从ModelLoader中构造LoadData,接着就能拿到DataFetcher,(关于ModelLoader/LoadData/DataFetcher之间的关系不在本次范围内,后面有机会再另写)通过它的loadData方法加载数据:

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

    如果在Resource中找不到需要的资源,那么startNext就会返回false,在runGenerators中就会进入循环体内:

    • 接着会重复上面执行getNextStage,由于现在Stage已经是RESOURCE_CACHE,所以接下来会返回DataCacheGenerator,执行逻辑和上面的ResourceCacheGenerator是一样的,如果还是没有找到需要的,进入循环体内。

    • 此时getNextStage会根据用于是否设置只从磁盘中获取资源,如果是就会通知失败,回调onLoadFailed;如果不是就设置当前Stage为Stage.SOURCE,接着往下走。

    • 状态就会进入循环内部的if条件逻辑里面,调用reschedule。

    • 在reschedule把runReason设置成SWITCH_TO_SOURCE_SERVICE,然后通过callback回调。

    • DecodeJob中的callback是EngineJob传递过来的,所以现在返回到EngineJob。

    • 在EngineJob中通过getActiveSourceExecutor切换到网络线程池中,执行DecodeJob,下面就准备开始发起网络请求。

    四、网络缓存

    在Stage.SOURCE阶段,通过getNextGenerator返回的是SourceGenerator,所以目前的currentGenerator就是它。

    流程还是一样的,SourceGenerator还是调用startNext方法,获取到对应的DataFetcher,这里其实是HttpUrlFetcher,发起网络请求。

    // DecodeJob.java 
    private void runGenerators() {
       ...
        while (!isCancelled && currentGenerator != null
            && !(isStarted = currentGenerator.startNext())) {
          stage = getNextStage(stage);
          currentGenerator = getNextGenerator();
     
          if (stage == Stage.SOURCE) {
            reschedule();
            return;
          }
        }
       ...
      }
     
      @Override
      public void reschedule() {
        runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
        callback.reschedule(this);
      }
     
    // EngineJob.java
      @Override
      public void reschedule(DecodeJob<?> job) {
        getActiveSourceExecutor().execute(job);
      }
    

    先缓一缓,本文其实到了上面已经可以结束了,Glide涉及到的五级缓存都已经涉及到了,是真的就可以结束了吗?不是的,网络请求回来和缓存还有关系吗?接着看到HttpUrlFetcher,下载成功后回调onDataReady,其中callback是SourceGenerator:

    // HttpUrlFetcher.java
    @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));
          }
        }
      }
     
    // EngineJob.java
      @Override
      public void reschedule(DecodeJob<?> job) {
        getActiveSourceExecutor().execute(job);
      }
    

    正常情况会进入if判断逻辑里面,赋值dataToCache,然后回调cb.reschedule,而cb就是DecodeJob构造SourceGenerator的时候传入,cb是DecodeJob。

    // SourceGenerator.java
      @Override
      public void onDataReady(Object data) {
        DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
        if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
          dataToCache = data;
          cb.reschedule();
        } else {
          cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
              loadData.fetcher.getDataSource(), originalKey);
        }
      }
    

    DecodeJob在reschedule回调EngineJob,最后还是回到SourceGenerator中的startNext()逻辑。

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

    和第一次进来的逻辑不一样,现在dataToCache != null,进入第一个if逻辑。

    在逻辑里面调用cacheData,逻辑很明显,保持数据到本地,然后会构造一个DataCacheGenerator。

    而DataCacheGenerator前面已经分析过了,就是用来加载本地原始数据的,这回会加载成功,返回true。

    // SourceGenerator.java
    @Override
      public boolean startNext() {
        if (dataToCache != null) {
          Object data = dataToCache;
          dataToCache = null;
          cacheData(data);
        }
         
        if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
          return true;
        }
        ...
      }
     
      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);
        } finally {
          loadData.fetcher.cleanup();
        }
     
        sourceCacheGenerator =
            new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
      }
    

    接下来就是一系列的回调了:

    DataCacheGenerator的startNext逻辑里面会给DataFetcher传递自身作为callback,在加载本地数据成功后回调onDataReady。

    // DataCacheGenerator
      @Override
      public boolean startNext() {
        ...
     
        loadData = null;
        boolean started = false;
        while (!started && hasNextModelLoader()) {
          ...
          if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
            started = true;
            loadData.fetcher.loadData(helper.getPriority(), this);
          }
        }
        return started;
      }
     
      @Override
      public void onDataReady(Object data) {
        cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
      }
    

    而cb现在是SourceGenerator传递过来,SourceGenerator再回调它自己的cb,是DecodeJob在构造它的时候传过来。

    // SourceGenerator.java
      @Override
      public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
          DataSource dataSource, Key attemptedKey) {
        cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
      }
     
    // DecodeJob.java
      @Override
      public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
          DataSource dataSource, Key attemptedKey) {
        this.currentSourceKey = sourceKey;
        this.currentData = data;
        this.currentFetcher = fetcher;
        this.currentDataSource = dataSource;
        this.currentAttemptingKey = attemptedKey;
        if (Thread.currentThread() != currentThread) {
          runReason = RunReason.DECODE_DATA;
          callback.reschedule(this);
        } else {
          try {
            decodeFromRetrievedData();
          } finally {
            GlideTrace.endSection();
          }
        }
      }
    

    在上面SourceGenerator把DecodeJob切换到ActiveSourceExecutor线程中执行,还记得一开始DecodeJob是在哪启动的吗?在EngineJob中启动,然后是把DecodeJob放到diskCacheExecutor中执行。

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

    所以上面在DecodeJob的onDataFetcherReady会走到第一个if逻辑里面,然后赋值runReason = RunReason.DECODE_DATA,再一次回调Engine.reschedule,将工作线程切换到ActiveSourceExecutor。

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

    然后还是走到DecodeJob, 现在会进入DECODE_DATA分支,在这里面会调用ResourceDecoder把数据解码:

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

    解码成功后调用notifyComplete(result, dataSource);

    private void notifyComplete(Resource<R> resource, DataSource dataSource) {
      setNotifiedOrThrow();
      callback.onResourceReady(resource, dataSource);
    }
    

    五、总结

    现在回答一下开头的几个问题。

    1、有几级缓存?五级,分别是什么?

    • 活动资源 (Active Resources)

    • 内存缓存 (Memory Cache)

    • 资源类型(Resource Disk Cache)

    • 原始数据 (Data Disk Cache)

    • 网络缓存

    2、Glide内存缓存之间是什么关系?

    专门画了一幅图表明这个关系,言简意赅。

    3、Glide本地文件IO和网络请求是一个线程吗?

    明显不是,本地IO通过diskCacheExecutor,而网络IO通过ActiveSourceExecutor

    4、Glide网络请求回来后数据直接返回给用户还是先存再返回?

    不是直接返回给用户,会在SourceGenerator中构造一个DataCacheGenerator来取数据。

    更多内容敬请关注 vivo 互联网技术 微信公众号

    注:转载文章请先与微信号:labs2020 联系

  • 相关阅读:
    O2O、B2B、C2C(通俗讲解)
    前端 $.parseJson()
    django反向解析传参
    从url(地址栏)获取参数:Jquery中getUrlParam()方法的使用
    Django:前后端分离后联调给前端传数据
    xpath 中 [<Element a at 3985984dj343>]
    sumafan:python爬虫多线程爬取数据小练习(附答案)
    window安装mysql(详细步骤)
    sqlserver从xlsx读取数据
    第一个kotlin程序
  • 原文地址:https://www.cnblogs.com/vivotech/p/11589012.html
Copyright © 2011-2022 走看看