溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Glide 緩存流程

發(fā)布時(shí)間:2020-06-20 09:27:33 來(lái)源:網(wǎng)絡(luò) 閱讀:211 作者:vivo互聯(lián)網(wǎng) 欄目:開(kāi)發(fā)技術(shù)

本文首發(fā)于 vivo互聯(lián)網(wǎng)技術(shù) 微信公眾號(hào)?
鏈接:https://mp.weixin.qq.com/s/cPLkefpEb3w12-uoiqzTig
作者:連凌能

Android上圖片加載的解決方案有多種,但是官方認(rèn)可的是Glide。Glide提供簡(jiǎn)潔易用的api,整個(gè)框架也方便擴(kuò)展,比如可以替換網(wǎng)絡(luò)請(qǐng)求庫(kù),同時(shí)也提供了完備的緩存機(jī)制,應(yīng)用層不需要自己去管理圖片的緩存與獲取,框架會(huì)分成內(nèi)存緩存,文件緩存和遠(yuǎn)程緩存。本文不會(huì)從簡(jiǎn)單的使用著手,會(huì)把重點(diǎn)放在緩存機(jī)制的分析上。

一、綜述

開(kāi)始之前,關(guān)于Glide緩存請(qǐng)先思考幾個(gè)問(wèn)題:

  • Glide有幾級(jí)緩存?

  • Glide內(nèi)存緩存之間是什么關(guān)系?

  • Glide本地文件IO和網(wǎng)絡(luò)請(qǐng)求是一個(gè)線程嗎?如果不是,怎么實(shí)現(xiàn)線程切換?

  • Glide網(wǎng)絡(luò)請(qǐng)求回來(lái)后數(shù)據(jù)直接返回給用戶還是先存再返回?

加載開(kāi)始入口從Engine.load()開(kāi)始,先看下對(duì)這個(gè)方法的注釋,

  • 會(huì)先檢查(Active Resources),如果有就直接返回,Active Resources沒(méi)有被引用的資源會(huì)放入Memory Cache,如果Active Resources沒(méi)有,會(huì)往下走。

  • 檢查Memory Cache中是否有需要的資源,如果有就返回,Memory Cache中沒(méi)有就繼續(xù)往下走。

  • 檢查當(dāng)前在運(yùn)行中的job中是否有改資源的下載,有就在現(xiàn)有的job中直接添加callback返回,不重復(fù)下載,當(dāng)然前提是計(jì)算得到的key是一致的,如果還是沒(méi)有,就會(huì)構(gòu)造一個(gè)新的job開(kāi)始新的工作。
* 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.

二、內(nèi)存緩存

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,這一步會(huì)從 ActiveResources 中加載資源,首先判斷是否使用內(nèi)存緩存,否的話返回null;否則到 ActiveResources 中取數(shù)據(jù):

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

接下來(lái)看下ActiveResources, 其實(shí)是用過(guò)弱引用保存使用過(guò)的資源。

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

  ...
}

成功取到數(shù)據(jù)后回調(diào)類(lèi)型也是內(nèi)存緩存:


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

接著回到Engine.load()中繼續(xù)看到focus 2,如果在cache中找到就是remove掉,然后返回EngineResource,其中需要EngineResource進(jìn)行acquire一下,這個(gè)后面再看,然后會(huì)把資源移到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接口的實(shí)現(xiàn),如果沒(méi)設(shè)置,默認(rèn)在build的時(shí)候是LruResourceCache, 也就是熟悉的LRU Cache:

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

再看下EngineResource,主要是對(duì)資源增加了引用計(jì)數(shù)的功能:

// 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后會(huì)判斷引用計(jì)數(shù)是否為0,如果是0就會(huì)回調(diào)onResourceReleased,在這里就是Engine,然后會(huì)把資源從ActiveResources中移除,資源默認(rèn)是可緩存的,因此會(huì)把資源放到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,如果引用計(jì)數(shù)為0并且還沒(méi)與回收,就會(huì)調(diào)用真正的Resource.recycle(),看其中的一個(gè)BitmapResource是怎么回收的,就是放到Bitmap池中,也是用的LRU Cache,這個(gè)和今天的主題不相關(guān),就不繼續(xù)往下拓展。

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

思路再拉到Engine.load()的流程中,接下來(lái)該看focus 3,這里再貼一下代碼,如果job已經(jīng)在運(yùn)行了,那么直接添加一個(gè)回調(diào)后返回LoadStatus,這個(gè)可以允許用戶取消任務(wù):

// 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, 到這里就需要?jiǎng)?chuàng)建后臺(tái)任務(wù)去拉取磁盤(pán)文件或者發(fā)起網(wǎng)絡(luò)請(qǐng)求。

三、磁盤(pán)緩存

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

先構(gòu)造兩個(gè)job,一個(gè)是EngineJob,另外一個(gè)DecodeJob,其中DecodeJob會(huì)根據(jù)需要解碼的資源來(lái)源分成下面幾個(gè)階段:

// 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,
  }

在構(gòu)造DecodeJob時(shí)會(huì)把狀態(tài)置為INITIALIZE。

構(gòu)造完兩個(gè) Job 后會(huì)調(diào)用 EngineJob.start(DecodeJob),首先會(huì)調(diào)用getNextStage來(lái)確定下一個(gè)階段,這里面跟DiskCacheStrategy這個(gè)傳入的磁盤(pán)緩存策略有關(guān)。

磁盤(pán)策略有下面幾種:

  • **ALL:?**緩存原始數(shù)據(jù)和轉(zhuǎn)換后的數(shù)據(jù)

  • **NONE:?**不緩存

  • **DATA:?**原始數(shù)據(jù),未經(jīng)過(guò)解碼或者轉(zhuǎn)換

  • **RESOURCE:?**緩存經(jīng)過(guò)解碼的數(shù)據(jù)

  • **AUTOMATIC(默認(rèn)):**根據(jù)`EncodeStrategy`和`DataSource`等條件自動(dòng)選擇合適的緩存方

默認(rèn)的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 會(huì)先返回Stage.RESOURCE_CACHE,然后在start中會(huì)返回diskCacheExecutor,然后開(kāi)始執(zhí)行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會(huì)回調(diào)run()開(kāi)始執(zhí)行, run()中調(diào)用runWrapped執(zhí)行工作,這里runReason還是RunReason.INITIALIZE ,根據(jù)前面的分析指導(dǎo)這里會(huì)獲得一個(gè)ResourceCacheGenerator,然后調(diào)用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 中,會(huì)調(diào)用 startNext,目前currentGenerator是ResourceCacheGenerator, 那么就是調(diào)用它的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(), 這里面就是重點(diǎn)邏輯了,首先從Registry中獲取支持資源類(lèi)型的ModelLoader(其中ModelLoader是在構(gòu)造Glide的時(shí)候傳進(jìn)去), 然后從ModelLoader中構(gòu)造LoadData,接著就能拿到DataFetcher,(關(guān)于ModelLoader/LoadData/DataFetcher之間的關(guān)系不在本次范圍內(nèi),后面有機(jī)會(huì)再另寫(xiě))通過(guò)它的loadData方法加載數(shù)據(jù):

@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就會(huì)返回false,在runGenerators中就會(huì)進(jìn)入循環(huán)體內(nèi):

  • 接著會(huì)重復(fù)上面執(zhí)行g(shù)etNextStage,由于現(xiàn)在Stage已經(jīng)是RESOURCE_CACHE,所以接下來(lái)會(huì)返回DataCacheGenerator,執(zhí)行邏輯和上面的ResourceCacheGenerator是一樣的,如果還是沒(méi)有找到需要的,進(jìn)入循環(huán)體內(nèi)。

  • 此時(shí)getNextStage會(huì)根據(jù)用于是否設(shè)置只從磁盤(pán)中獲取資源,如果是就會(huì)通知失敗,回調(diào)onLoadFailed;如果不是就設(shè)置當(dāng)前Stage為Stage.SOURCE,接著往下走。

  • 狀態(tài)就會(huì)進(jìn)入循環(huán)內(nèi)部的if條件邏輯里面,調(diào)用reschedule。

  • 在reschedule把runReason設(shè)置成SWITCH_TO_SOURCE_SERVICE,然后通過(guò)callback回調(diào)。

  • DecodeJob中的callback是EngineJob傳遞過(guò)來(lái)的,所以現(xiàn)在返回到EngineJob。

  • 在EngineJob中通過(guò)getActiveSourceExecutor切換到網(wǎng)絡(luò)線程池中,執(zhí)行DecodeJob,下面就準(zhǔn)備開(kāi)始發(fā)起網(wǎng)絡(luò)請(qǐng)求。

四、網(wǎng)絡(luò)緩存

在Stage.SOURCE階段,通過(guò)getNextGenerator返回的是SourceGenerator,所以目前的currentGenerator就是它。

流程還是一樣的,SourceGenerator還是調(diào)用startNext方法,獲取到對(duì)應(yīng)的DataFetcher,這里其實(shí)是HttpUrlFetcher,發(fā)起網(wǎng)絡(luò)請(qǐng)求。

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

先緩一緩,本文其實(shí)到了上面已經(jīng)可以結(jié)束了,Glide涉及到的五級(jí)緩存都已經(jīng)涉及到了,是真的就可以結(jié)束了嗎?不是的,網(wǎng)絡(luò)請(qǐng)求回來(lái)和緩存還有關(guān)系嗎?接著看到HttpUrlFetcher,下載成功后回調(diào)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);
  }

正常情況會(huì)進(jìn)入if判斷邏輯里面,賦值dataToCache,然后回調(diào)cb.reschedule,而cb就是DecodeJob構(gòu)造SourceGenerator的時(shí)候傳入,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回調(diào)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);
  }

和第一次進(jìn)來(lái)的邏輯不一樣,現(xiàn)在dataToCache != null,進(jìn)入第一個(gè)if邏輯。

在邏輯里面調(diào)用cacheData,邏輯很明顯,保持?jǐn)?shù)據(jù)到本地,然后會(huì)構(gòu)造一個(gè)DataCacheGenerator。

而DataCacheGenerator前面已經(jīng)分析過(guò)了,就是用來(lái)加載本地原始數(shù)據(jù)的,這回會(huì)加載成功,返回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);
  }

接下來(lái)就是一系列的回調(diào)了:

DataCacheGenerator的startNext邏輯里面會(huì)給DataFetcher傳遞自身作為callback,在加載本地?cái)?shù)據(jù)成功后回調(diào)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現(xiàn)在是SourceGenerator傳遞過(guò)來(lái),SourceGenerator再回調(diào)它自己的cb,是DecodeJob在構(gòu)造它的時(shí)候傳過(guò)來(lái)。

// 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線程中執(zhí)行,還記得一開(kāi)始DecodeJob是在哪啟動(dòng)的嗎?在EngineJob中啟動(dòng),然后是把DecodeJob放到diskCacheExecutor中執(zhí)行。

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

所以上面在DecodeJob的onDataFetcherReady會(huì)走到第一個(gè)if邏輯里面,然后賦值runReason = RunReason.DECODE_DATA,再一次回調(diào)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, 現(xiàn)在會(huì)進(jìn)入DECODE_DATA分支,在這里面會(huì)調(diào)用ResourceDecoder把數(shù)據(jù)解碼:

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

解碼成功后調(diào)用notifyComplete(result, dataSource);

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

五、總結(jié)

現(xiàn)在回答一下開(kāi)頭的幾個(gè)問(wèn)題。

1、有幾級(jí)緩存?五級(jí),分別是什么?

  • 活動(dòng)資源 (Active Resources)

  • 內(nèi)存緩存 (Memory Cache)

  • 資源類(lèi)型(Resource Disk Cache)

  • 原始數(shù)據(jù) (Data Disk Cache)

  • 網(wǎng)絡(luò)緩存

2、Glide內(nèi)存緩存之間是什么關(guān)系?

專門(mén)畫(huà)了一幅圖表明這個(gè)關(guān)系,言簡(jiǎn)意賅。

Glide 緩存流程

3、Glide本地文件IO和網(wǎng)絡(luò)請(qǐng)求是一個(gè)線程嗎?

明顯不是,本地IO通過(guò)diskCacheExecutor,而網(wǎng)絡(luò)IO通過(guò)ActiveSourceExecutor

4、Glide網(wǎng)絡(luò)請(qǐng)求回來(lái)后數(shù)據(jù)直接返回給用戶還是先存再返回?

不是直接返回給用戶,會(huì)在SourceGenerator中構(gòu)造一個(gè)DataCacheGenerator來(lái)取數(shù)據(jù)。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI