Glide4.9圖片框架源碼(五)之存儲(chǔ)緩存和回調(diào)顯示

上一節(jié)我們說(shuō)到了如何獲取磁盤緩存和開(kāi)啟圖片請(qǐng)求的,需要回顧的同學(xué)可以繼續(xù)查看:

Glide4.9圖片框架源碼(四)之獲取磁盤緩存和圖片請(qǐng)求

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

上一節(jié)我們講述了HttpUrlFetcher調(diào)用loadData請(qǐng)求圖片數(shù)據(jù)并且返回圖片對(duì)應(yīng)的InputStream蜈抓,那么這一節(jié)我們著重看下是如何將InputStream回調(diào)到最初的ViewTarget并且顯示的启绰,所以這里我們要看仔細(xì)每一個(gè)回調(diào)監(jiān)聽(tīng)。這里我們看到調(diào)用了callback.onDataReady(result)方法沟使,那么callback是由loaddata傳進(jìn)來(lái)的委可,我們返回再看看是怎么入?yún)⒌?

SourceGenerator

  @Override
  public boolean startNext() {
    .........
    .........
    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;
  }

可以看到SourceGenerator傳入的是this,所以上面的onDataReady方法回調(diào)到SourceGenerator的onDataReady方法腊嗡,我們繼續(xù)看回調(diào):

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

這里isDataCacheable是去判讀是否能緩存到磁盤着倾,如果不能,就繼續(xù)往上回調(diào)燕少,如果可以緩存卡者,那么將當(dāng)前的inputstream賦值給dataToCache,然后調(diào)用cb.reschedule()客们,我們來(lái)看看這個(gè)cb監(jiān)聽(tīng)是什么時(shí)候傳進(jìn)來(lái)的:

  private final FetcherReadyCallback cb;

  private int loadDataListIndex;
  private DataCacheGenerator sourceCacheGenerator;
  private Object dataToCache;
  private volatile ModelLoader.LoadData<?> loadData;
  private DataCacheKey originalKey;

  SourceGenerator(DecodeHelper<?> helper, FetcherReadyCallback cb) {
    this.helper = helper;
    this.cb = cb;
  }

可以看到FetcherReadyCallback是在SourceGenerator初始化的時(shí)候傳進(jìn)來(lái)的崇决,那么看下SourceGenerator的初始化方法:

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

可以看到是在DecodeJob的getNextGenerator方法中去初始化的,所以我們看看他的reschedule方法:

  @Override
  public void reschedule() {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);
  }
 @Override
  public void reschedule(DecodeJob<?> job) {
    getActiveSourceExecutor().execute(job);
  }

可以看到還是重新執(zhí)行了DecodeJob線程镶摘,直接去加載SourceGenerator嗽桩,我們跟進(jìn)去看一下源碼:

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

由于我們之前已經(jīng)分析過(guò)這一塊的startNext方法,所以這里調(diào)用的是SourceGenerator的startNext凄敢,可以看到第一行直接判讀按dataToCache非空碌冶,dataToCache在剛才獲取input stream的時(shí)候已經(jīng)賦值給dataToCache,所以這里直接執(zhí)行cacheData方法去緩存數(shù)據(jù)涝缝。cacheData里面的helper.getSourceEncoder是通過(guò)傳入的data類型扑庞,返回一個(gè)Encoder對(duì)應(yīng)的解碼器。然后又生成了一個(gè)DataCacheWriter包裝類拒逮,傳入了解碼器和dataToCache流數(shù)據(jù)罐氨,調(diào)用 helper.getDiskCache().put方法,我們跟進(jìn)去看下put方法:

@Override
  public void put(Key key, Writer writer) {
    String safeKey = safeKeyGenerator.getSafeKey(key);
    writeLocker.acquire(safeKey);
    try {
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Put: Obtained: " + safeKey + " for for Key: " + key);
      }
      try {
        DiskLruCache diskCache = getDiskCache();
        Value current = diskCache.get(safeKey);
        if (current != null) {
          return;
        }
        DiskLruCache.Editor editor = diskCache.edit(safeKey);
        if (editor == null) {
          throw new IllegalStateException("Had two simultaneous puts for: " + safeKey);
        }
        try {
          File file = editor.getFile(0);
          if (writer.write(file)) {
            editor.commit();
          }
        } finally {
          editor.abortUnlessCommitted();
        }
      } catch (IOException e) {
        if (Log.isLoggable(TAG, Log.WARN)) {
          Log.w(TAG, "Unable to put to disk cache", e);
        }
      }
    } finally {
      writeLocker.release(safeKey);
    }
  }

可以看到傳進(jìn)來(lái)的writer調(diào)用了write(file)滩援,這里我們就不跟進(jìn)去了栅隐,就是一般的讀取流然后存儲(chǔ)到文件,這里就把數(shù)據(jù)緩存到了硬盤緩存玩徊,回到哦最初的cacheData方法租悄,重新去new DataCacheGenerator并且傳入的監(jiān)聽(tīng)是this,sourceCacheGenerator = new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this)恩袱,然后繼續(xù)調(diào)用startStage中的sourceCacheGenerator.startNext泣棋,則去調(diào)用了DataCacheGenerator的startNext方法,繼續(xù)看:

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

這里為什么又去執(zhí)行一次呢畔塔,其實(shí)這里是從磁盤緩存中去讀取剛剛我們緩存的數(shù)據(jù)潭辈,那么這里的loadData.fetcher.loadData則去緩存中加載緩存圖片文件并且再去回調(diào)鸯屿,我們看下FileFetcher的loadData方法:

    @Override
    public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
      try {
        data = opener.open(file);
      } catch (FileNotFoundException e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
          Log.d(TAG, "Failed to open file", e);
        }
        callback.onLoadFailed(e);
        return;
      }
      callback.onDataReady(data);
    }

直接通過(guò)opener.open去獲取文件,加載的回調(diào)就在DataCacheGenerator的onDataReady中

@Override
  public void onDataReady(Object data) {
    cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
  }
 @Override
  public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
      DataSource dataSource, Key attemptedKey) {
    cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
  }
 @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 {
      GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
      try {
        decodeFromRetrievedData();
      } finally {
        GlideTrace.endSection();
      }
    }
  }

繼續(xù)回調(diào)依然是到上層的sourceCacheGenerator的onDataFetcherReady中把敢,然后sourceCacheGenerator回調(diào)到Decode中的onDataFetcherReady方法寄摆,這里的currentThread從開(kāi)啟子線程執(zhí)行DecodeJobs后沒(méi)有做過(guò)線程切換,所以依然是當(dāng)前線程技竟,所以執(zhí)行callback.reschedule(this)方法并且賦值runReason為DECODE_DATA準(zhǔn)備再次執(zhí)行Decode線程去解析磁盤緩存回調(diào)的數(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);
    }
  }
private void decodeFromRetrievedData() {
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logWithTimeAndKey("Retrieved data", startFetchTime,
          "data: " + currentData
              + ", cache key: " + currentSourceKey
              + ", fetcher: " + currentFetcher);
    }
    Resource<R> resource = null;
    try {
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }

由于是 runReason = RunReason.DECODE_DATA冰肴,所以直接執(zhí)行decodeFromRetrievedData屈藐,這里調(diào)用了decodeFromData去解析數(shù)據(jù)榔组,并返回一個(gè)Resource,這里的Resource就是加載和解析過(guò)后的數(shù)據(jù)联逻,不去細(xì)究搓扯,直接看回調(diào)notifyEncodeAndRelease方法:

  private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    if (resource instanceof Initializable) {
      ((Initializable) resource).initialize();
    }
    Resource<R> result = resource;
    LockedResource<R> lockedResource = null;
    if (deferredEncodeManager.hasResourceToEncode()) {
      lockedResource = LockedResource.obtain(resource);
      result = lockedResource;
    }
    notifyComplete(result, dataSource);
    stage = Stage.ENCODE;
    try {
      if (deferredEncodeManager.hasResourceToEncode()) {
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      if (lockedResource != null) {
        lockedResource.unlock();
      }
    }
    onEncodeComplete();
  }

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

這里直接看回調(diào)notifyComplete方法,調(diào)用了callback.onResourceReady方法包归,可能我們已經(jīng)忘了這里的callback到底是什么時(shí)候傳進(jìn)來(lái)的锨推,來(lái)看一下:

 DecodeJob<R> init(
      GlideContext glideContext,
      Object model,
      EngineKey loadKey,
      Key signature,
      int width,
      int height,
      Class<?> resourceClass,
      Class<R> transcodeClass,
      Priority priority,
      DiskCacheStrategy diskCacheStrategy,
      Map<Class<?>, Transformation<?>> transformations,
      boolean isTransformationRequired,
      boolean isScaleOnlyOrNoTransform,
      boolean onlyRetrieveFromCache,
      Options options,
      Callback<R> callback,
      int order) {
    ......
    this.callback = callback;
    this.order = order;
    this.runReason = RunReason.INITIALIZE;
    this.model = model;
    return this;
  }

可以看到調(diào)用的是DecodeJob的initial方法擎浴,最終發(fā)現(xiàn)這里的callback其實(shí)就是EngineJob本身换薄,所以看一看EngineJob的onResourceReady方法:

  @Override
  public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    synchronized (this) {
      this.resource = resource;
      this.dataSource = dataSource;
    }
    notifyCallbacksOfResult();
  }

這里分別賦值了resource和dataSource,這兩個(gè)變量就是我們說(shuō)到的磁盤緩存的兩種類型奠旺,繼續(xù)看notifyCallbacksOfResult方法:

void notifyCallbacksOfResult() {
    ResourceCallbacksAndExecutors copy;
    Key localKey;
    EngineResource<?> localResource;
    synchronized (this) {
      stateVerifier.throwIfRecycled();
      if (isCancelled) {
        resource.recycle();
        release();
        return;
      } else if (cbs.isEmpty()) {
        throw new IllegalStateException("Received a resource without any callbacks to notify");
      } else if (hasResource) {
        throw new IllegalStateException("Already have resource");
      }
      engineResource = engineResourceFactory.build(resource, isCacheable);
      hasResource = true;
      copy = cbs.copy();
      incrementPendingCallbacks(copy.size() + 1);
      localKey = key;
      localResource = engineResource;
    }
    listener.onEngineJobComplete(this, localKey, localResource);
    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
    decrementPendingCallbacks();
  }
  @Override
  public synchronized void onEngineJobComplete(
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    // A null resource indicates that the load failed, usually due to an exception.
    if (resource != null && resource.isMemoryCacheable()) {
      activeResources.activate(key, resource);
    }

    jobs.removeIfCurrent(key, engineJob);
  }

  synchronized void activate(Key key, EngineResource<?> resource) {
    ResourceWeakReference toPut =
        new ResourceWeakReference(
            key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
    ResourceWeakReference removed = activeEngineResources.put(key, toPut);
    if (removed != null) {
      removed.reset();
    }
  }

ResourceCallbacksAndExecutors 這里存儲(chǔ)的是回調(diào)的線程池執(zhí)行器厦幅,我們先看后面沾鳄,有一個(gè)EngineResource對(duì)象,還記得之前我們的內(nèi)存緩存嗎确憨,對(duì)了译荞,這里就是新建一個(gè)緩存對(duì)象,下面的engineResourceFactory.build方法休弃,就是通過(guò)傳入已經(jīng)加載完成的resource生成EngineResource內(nèi)存緩存吞歼。繼續(xù)看listener的onEngineJobComplete方法,傳入了key和engineResource塔猾,這里我們看到了兩個(gè)熟悉的變量篙骡,isMemoryCacheable和activeResources,就是判斷是否緩存到內(nèi)存丈甸,如果是糯俗,那么直接通過(guò)activeResources的activate方法構(gòu)造出ResourceWeakReference并且存儲(chǔ)到弱引用緩存。

回到上面的notifyCallbacksOfResult老虫,存儲(chǔ)到內(nèi)存緩存后叶骨,應(yīng)該開(kāi)始回調(diào)到最初的加載的地方并且設(shè)置圖片了,我們繼續(xù)看下entry.executor.execute(new CallResourceReady(entry.cb))方法祈匙,首先entry里存儲(chǔ)的就是copy的ResourceCallbackAndExecutor忽刽,而copy是通過(guò)調(diào)用cbs的copy方法獲取的天揖,我們跟進(jìn)去看一下

 static final class ResourceCallbacksAndExecutors
      implements Iterable<ResourceCallbackAndExecutor> {
    private final List<ResourceCallbackAndExecutor> callbacksAndExecutors;

    ResourceCallbacksAndExecutors() {
      this(new ArrayList<ResourceCallbackAndExecutor>(2));
    }

    void add(ResourceCallback cb, Executor executor) {
      callbacksAndExecutors.add(new ResourceCallbackAndExecutor(cb, executor));
    }

    void remove(ResourceCallback cb) {
      callbacksAndExecutors.remove(defaultCallbackAndExecutor(cb));
    }

    ResourceCallbacksAndExecutors copy() {
      return new ResourceCallbacksAndExecutors(new ArrayList<>(callbacksAndExecutors));
    }

發(fā)現(xiàn)callbacksAndExecutors就是ResourceCallbacksAndExecutors的一個(gè)變量,通過(guò)調(diào)用add方法添加Executor跪帝,那么我們就跟進(jìn)去看在什么時(shí)候添加了這個(gè)回調(diào)執(zhí)行器今膊。

 synchronized void addCallback(final ResourceCallback cb, Executor callbackExecutor) {
    stateVerifier.throwIfRecycled();
    cbs.add(cb, callbackExecutor);
    if (hasResource) {
      incrementPendingCallbacks(1);
      callbackExecutor.execute(new CallResourceReady(cb));
    } else if (hasLoadFailed) {
      incrementPendingCallbacks(1);
      callbackExecutor.execute(new CallLoadFailed(cb));
    } else {
      Preconditions.checkArgument(!isCancelled, "Cannot add callbacks to a cancelled EngineJob");
    }
  }

    jobs.put(key, engineJob);
    engineJob.addCallback(cb, callbackExecutor);
    engineJob.start(decodeJob);

我們發(fā)現(xiàn),callbackExecutor回調(diào)執(zhí)行器在請(qǐng)求圖片資源伞剑,開(kāi)啟decodeJob的時(shí)候就添加進(jìn)來(lái)了斑唬,繼續(xù)往上跟,看清楚callbackExecutor的真正對(duì)象是什么:

  <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      Executor callbackExecutor) {
    return into(target, targetListener, /*options=*/ this, callbackExecutor);
  }

  public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
    return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
  }

 private static final Executor MAIN_THREAD_EXECUTOR =
      new Executor() {
        private final Handler handler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(@NonNull Runnable command) {
          handler.post(command);
        }
      };

跟到最初始的地方我們發(fā)現(xiàn)黎泣,原來(lái)在調(diào)用into方法的時(shí)候就已經(jīng)傳入了一個(gè)主線程的執(zhí)行器恕刘,所以回到我們存儲(chǔ)內(nèi)存緩存即將回調(diào)的地方:

 for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }

public void run() {
      synchronized (cb.getLock()) {
        synchronized (EngineJob.this) {
          if (cbs.contains(cb)) {
            engineResource.acquire();
            callCallbackOnResourceReady(cb);
            removeCallback(cb);
          }
          decrementPendingCallbacks();
        }
      }
    }

  void callCallbackOnResourceReady(ResourceCallback cb) {
    try {
      cb.onResourceReady(engineResource, dataSource);
    } catch (Throwable t) {
      throw new CallbackException(t);
    }
  }

所以這里的executor,必定存在主線程的MAIN_THREAD_EXECUTOR抒倚,所以在這里實(shí)現(xiàn)了線程切換褐着,將數(shù)據(jù)回調(diào)到了主線程,我們直接看CallResourceReady的run方法托呕,engineResource.acquire()開(kāi)啟了引用計(jì)數(shù)含蓉,表示當(dāng)前內(nèi)存緩存已經(jīng)引用了,繼續(xù)調(diào)用callCallbackOnResourceReady方法项郊,這里的cb.onResourceReady方法就回調(diào)到了singleRequest中馅扣,我們跟進(jìn)去看:

public void onResourceReady(Resource<?> resource, DataSource dataSource) {
    stateVerifier.throwIfRecycled();
    Resource<?> toRelease = null;
    try {
      synchronized (requestLock) {
        loadStatus = null;
          ......
          ......
        if (!canSetResource()) {
          toRelease = resource;
          this.resource = null;
          status = Status.COMPLETE;
          return;
        }
        onResourceReady((Resource<R>) resource, (R) received, dataSource);
      }
    } finally {
      if (toRelease != null) {
        engine.release(toRelease);
      }
    }
  }

private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;
    ......
    isCallingCallbacks = true;
    try {
      boolean anyListenerHandledUpdatingTarget = false;
      if (requestListeners != null) {
        for (RequestListener<R> listener : requestListeners) {
          anyListenerHandledUpdatingTarget |=
              listener.onResourceReady(result, model, target, dataSource, isFirstResource);
        }
      }
      anyListenerHandledUpdatingTarget |=
          targetListener != null
              && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
      if (!anyListenerHandledUpdatingTarget) {
        Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
        target.onResourceReady(result, animation);
      }
    } finally {
      isCallingCallbacks = false;
    }
    notifyLoadSuccess();
  }

onResourceReady省略的地方做了一些判斷和失敗的回調(diào),可以忽略着降,我們直接看下面的onResourceReady方法差油,requestListeners和targetListener是最初我們自己是否添加過(guò)加載監(jiān)聽(tīng),如果設(shè)置過(guò)監(jiān)聽(tīng)鹊碍,這里就會(huì)回調(diào)厌殉,我們沒(méi)有設(shè)置直接看target.onResourceReady方法,還記得我們的imageview設(shè)置到哪里了么侈咕,就是target中公罕,因此這里就將數(shù)據(jù)源回調(diào)到了image view中,因此這里調(diào)用imageviewtarget的onResourceReady方法:

public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      maybeUpdateAnimatable(resource);
    }
  }
 private void setResourceInternal(@Nullable Z resource) {
    // non-null Callback before starting it.
    setResource(resource);
    maybeUpdateAnimatable(resource);
  }

DrawableImageViewTarget
protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }

這里的setResource方法也就調(diào)用了DrawableImageViewTarget的setResource方法耀销,可以看到又調(diào)用了view的setImageDrawable方法楼眷,將圖片設(shè)置到view上了,到這里從加載數(shù)據(jù)結(jié)束到存儲(chǔ)到緩存并加載到view上的流程就分析結(jié)束了熊尉。

總結(jié)

HttpUrlFetcher 請(qǐng)求玩圖片后返回給SourceGenerator圖片的input stream罐柳,然后重新執(zhí)行DecodeJob,調(diào)用SourceGenerator的startNext的cacheData方法去緩存數(shù)據(jù)到磁盤然后調(diào)用DataCacheGenerator的startNext方法又去磁盤緩存中加載數(shù)據(jù)狰住,加載完成后再次執(zhí)行DecodeJob方法张吉,去decode解析磁盤緩存得到resource對(duì)象,完成后回調(diào)到EngineJob中催植,然后將resource存儲(chǔ)到弱引用內(nèi)存緩存中肮蛹,引用計(jì)數(shù)加一勺择,調(diào)用主線程的handler將數(shù)據(jù)回調(diào)到主線程的Target中加載并顯示圖片。執(zhí)行流程就是SourceGenerator-->DataCacheGenerator-->DecodeJob-->EngineJob-->SingleRequest-->Target伦忠。到這里整個(gè)圖片的加載流程就分析結(jié)束了省核,下一節(jié)我們?cè)僦v講面試中Glide源碼可能關(guān)注的一些問(wèn)題~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市昆码,隨后出現(xiàn)的幾起案子气忠,更是在濱河造成了極大的恐慌,老刑警劉巖赋咽,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旧噪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡冬耿,警方通過(guò)查閱死者的電腦和手機(jī)舌菜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門萌壳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)亦镶,“玉大人,你說(shuō)我怎么就攤上這事袱瓮$凸牵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵尺借,是天一觀的道長(zhǎng)绊起。 經(jīng)常有香客問(wèn)我,道長(zhǎng)燎斩,這世上最難降的妖魔是什么虱歪? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮栅表,結(jié)果婚禮上笋鄙,老公的妹妹穿的比我還像新娘。我一直安慰自己怪瓶,他們只是感情好萧落,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著洗贰,像睡著了一般找岖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敛滋,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天许布,我揣著相機(jī)與錄音,去河邊找鬼绎晃。 笑死蜜唾,一個(gè)胖子當(dāng)著我的面吹牛帖旨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播灵妨,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼解阅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了泌霍?” 一聲冷哼從身側(cè)響起货抄,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎朱转,沒(méi)想到半個(gè)月后蟹地,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡藤为,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年怪与,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缅疟。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡分别,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出存淫,到底是詐尸還是另有隱情耘斩,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布桅咆,位于F島的核電站括授,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏岩饼。R本人自食惡果不足惜荚虚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望籍茧。 院中可真熱鬧版述,春花似錦、人聲如沸硕糊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)简十。三九已至檬某,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間螟蝙,已是汗流浹背恢恼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胰默,地道東北人场斑。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓漓踢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親漏隐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子喧半,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344