對著那Glide最新版本就是一頓暴講 2(數(shù)據(jù)加載篇)

今天我要對著 Glide數(shù)據(jù)加載 進(jìn)行一頓暴講耘沼,我要給它講的 锃光瓦亮韧衣,我要給它講的 烏漆嘛黑县遣。

上回針對Gldie的準(zhǔn)備工作進(jìn)行了詳細(xì)分解,從使用到原理汹族,從整體到局部整體噴了一遍萧求。

還是不太懂得小伙伴可以飛機(jī)直達(dá) 對著那Glide最新版本就是一頓暴講 ,因為之前的處理不太了解的話顶瞒,會對接下來的神邏輯產(chǎn)生困擾夸政。

當(dāng)然了,如果此刻確實困了的同學(xué)榴徐,我一定給你一個 好夢...

image

以下的幾個標(biāo)題是在我寫完整篇后重新總結(jié)的守问,這樣會比較有針對性。

    1. 緩存的讀取
    1. 請求的啟動
    1. 請求成功后的數(shù)據(jù)處理(InputStream的處理)
    1. 數(shù)據(jù)解碼
    1. 數(shù)據(jù)轉(zhuǎn)換
    1. 圖片顯示與那一堆回調(diào)
    1. 緩存的維護(hù)與使用流程

來坑资,我們開始搞...


兄臺準(zhǔn)備好了么耗帕?我要以Glide數(shù)據(jù)加載,祭我大諾克薩斯袱贮!

1. 緩存的讀取

緩存分為弱引用緩存和LRU緩存仿便。
前景回顧下,我們上文已經(jīng)分析到了 SingleReques.begin()

public void begin() {
    synchronized (requestLock) {
      assertNotCallingCallbacks();
      stateVerifier.throwIfRecycled();
      startTime = LogTime.getLogTime();
    ...
      //判斷寬高是狗有效攒巍。
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        onSizeReady(overrideWidth, overrideHeight);
      } else {
        //寬高無效嗽仪,需要等待有效
        target.getSize(this);
      }

      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)&& canNotifyStatusChanged()) {
        target.onLoadStarted(getPlaceholderDrawable());
      }  
    }
  }

  public void onSizeReady(int width, int height) {
  ...
    //這個方法上次沒有展開分析,而這個方法即是整個加載的核心柒莉。
    loadStatus = engine.load(
        glideContext,
        model,
        requestOptions.getSignature(),
        this.width,
        this.height,
        requestOptions.getResourceClass(),
        transcodeClass,
        priority,
        requestOptions.getDiskCacheStrategy(),
        requestOptions.getTransformations(),
        requestOptions.isTransformationRequired(),
        requestOptions.isScaleOnlyOrNoTransform(),
        requestOptions.getOptions(),
        requestOptions.isMemoryCacheable(),
        requestOptions.getUseUnlimitedSourceGeneratorsPool(),
        requestOptions.getUseAnimationPool(),
        requestOptions.getOnlyRetrieveFromCache(),
        this);
  ...
  }
 

engine對象是在Glide.with()進(jìn)行初始化的闻坚,此時使用的engine即是Glide.with()初始化的對象。
我們跟進(jìn)看下 engine.load() 具體做了什么操作兢孝。

  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,
      Executor callbackExecutor) {
    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
    //構(gòu)建唯一key窿凤,注意構(gòu)建過程的入?yún)ⅲ瑢捀呖缧罚刂扶ㄊ猓葏?shù)。
    EngineKey key = keyFactory.buildKey(model,  signature, width, height, transformations, resourceClass,
                                        transcodeClass, options);

    EngineResource<?> memoryResource;
    synchronized (this) {
      //加鎖喷市,從內(nèi)存中獲取,可直接看后續(xù)的跟進(jìn).
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);

      if (memoryResource == null) {
        //如果緩存中都沒有找到相种,則調(diào)用waitForExistingOrStartNewJob(),后續(xù)單獨展開分析。
        return waitForExistingOrStartNewJob(
            glideContext,
            model,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            options,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache,
            cb,
            callbackExecutor,
            key,
            startTime);
      }
    }

    // 如果 engine lock 則避免回調(diào)寝并,防止調(diào)用方死鎖箫措。
    cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
    return null;
  }

  private EngineResource<?> loadFromMemory(EngineKey key, boolean isMemoryCacheable, long startTime) {
     //isMemoryCacheable默認(rèn)情況下為true
     //當(dāng)配置中設(shè)置了RequestOptions.skipMemoryCacheOf()的值的話:
     //1.當(dāng)skipMemoryCacheOf傳入true時為false,即關(guān)閉內(nèi)存緩存
     //2.當(dāng)skipMemoryCacheOf傳入false時為true,即開啟內(nèi)存緩存
    if (!isMemoryCacheable) {
      return null;
    }
    //字面意思是從弱引用列表中獲取
    EngineResource<?> active = loadFromActiveResources(key);
    if (active != null) { 
      return active;
    }
    //如果弱引用Map中沒有獲取到則從緩存中獲取
    EngineResource<?> cached = loadFromCache(key);
    if (cached != null) { 
      return cached;
    }
    return null;
  }
  //從弱引用Map中獲取數(shù)據(jù)
  private EngineResource<?> loadFromActiveResources(Key key) {
    EngineResource<?> active = activeResources.get(key);
    if (active != null) {
      active.acquire();
    }

    return active;
  }

private EngineResource<?> loadFromCache(Key key) {
    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
      //判斷當(dāng)前對象是否被回收衬潦,如果沒有被回收引用計數(shù)+1斤蔓,如已回收則拋異常。
      cached.acquire();
      //添加到弱引用Map中镀岛,方便下次使用弦牡。
      activeResources.activate(key, cached);
    }
    return cached;
  }

private EngineResource<?> getEngineResourceFromCache(Key key) {
    //雖然調(diào)用的是cache.remove(key),但是刪除成功后會返回刪除的對象漂羊,如果沒有找到刪除對象則返回null
   //此時的cache為LruResourceCache驾锰,是在Glide初始化時由GlideBuilder.build()時創(chuàng)建的。
    Resource<?> cached = cache.remove(key);

    final EngineResource<?> result;
    //這些if判斷目前還不是特別清楚邏輯走越,需要知道cache數(shù)據(jù)怎么添加的才能清楚這些椭豫。
    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, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
    }
    return result;
  }

雖然現(xiàn)在不清楚緩存的數(shù)據(jù)在哪個位置添加的,但是我們清楚了緩存的調(diào)用順序旨指,先弱引用赏酥,后Lru,最后走網(wǎng)絡(luò)谆构。

  • 首先通過 keyFactory.buildKey() 生成圖片的唯一key裸扶。
  • 其次 loadFromActiveResources() 從activeResources(弱引用Map)中查找對應(yīng)的資源。
  • 最后通過 loadFromCache()->getEngineResourceFromCache()從LruResourceCache刪除資源 搬素,如果刪除成功則返回原數(shù)據(jù)呵晨,并將數(shù)據(jù)添加到activeResources中,同時引用技術(shù)器+1蔗蹋。(此處用刪除代替查詢何荚,一舉兩得。)

意義與生俱來猪杭,我們只需遵其而行,真意妥衣,環(huán)繞四周皂吮。動則得福。

2. 請求的啟動

如果弱引用和Lru均沒獲取到數(shù)據(jù)需通過 waitForExistingOrStartNewJob()方法 進(jìn)行處理税手,我們繼續(xù)蜂筹。

private <R> LoadStatus waitForExistingOrStartNewJob(
      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,
      Executor callbackExecutor,
      EngineKey key,
      long startTime) {

    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是一個Runnable對象,可以看到此處decodeJob被當(dāng)作參數(shù)傳入了 engineJob.start(decodeJob)中
    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);
    //添加回調(diào)和回調(diào)線程池芦倒。
    engineJob.addCallback(cb, callbackExecutor);
    //啟動decodeJob這個Runnable
    engineJob.start(decodeJob);

    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);
  }

看到這里感覺懵逼很正常艺挪,我看到這也是懵逼的,咱們繼續(xù)兵扬,看看 engineJob.start(decodeJob);干了什么事

public synchronized void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    //獲取GlideExecutor 線程池
    GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
     //開始執(zhí)行decodeJob這個Runnable
    executor.execute(decodeJob);
  }
  /**
   * DecodeJob.willDecodeFromCache()方法
   * 如果從硬盤解碼資源返回true麻裳,從緩存中解碼返回false口蝠。
   */
  boolean willDecodeFromCache() {
    Stage firstStage = getNextStage(Stage.INITIALIZE);
    return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
  }

真像就在decodeJob的run方法中.

  public void run() {
    // This should be much more fine grained, but since Java's thread pool implementation silently
    // swallows all otherwise fatal exceptions, this will at least make it obvious to developers
    // that something is failing.
    GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
    //try語句中的方法可能使currentfeetcher無效,因此在此處設(shè)置一個局部變量以確保以任何方式清除該fetcher津坑。
    DataFetcher<?> localFetcher = currentFetcher;
    try {
       //如果狀態(tài)為已取消的狀態(tài)則調(diào)用notifyFailed(),連帶調(diào)用callback.onLoadFailed(e);
      if (isCancelled) {
        notifyFailed();
        return;
      }
      //關(guān)鍵方法
      runWrapped();
    } catch (CallbackException e) {
      // 如果不是由Glide控制的回調(diào)拋出異常妙蔗,我們應(yīng)該避免下面的Glide特定調(diào)試邏輯。
      throw e;
    } catch (Throwable t) {
      // 捕捉可拋出的對象疆瑰,并不是處理oom的例外眉反。我們在GlideExecutor中使用了.submit(),因此我們不會通過這樣做悄悄地隱藏崩潰穆役。
      //但是寸五,我們要確保在加載失敗時始終通知回調(diào)。如果沒有這個通知耿币,未經(jīng)處理的拋出文件永遠(yuǎn)不會通知相應(yīng)的回調(diào)
     //這可能會導(dǎo)致負(fù)載永遠(yuǎn)靜默掛起播歼,這種情況對于在后臺線程上使用Futures的用戶尤其不利。
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG,"DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage, t);
      }
      // 如果當(dāng)前狀態(tài)是encoding掰读,則直接callback.onLoadFailed(e);
      if (stage != Stage.ENCODE) {
        throwables.add(t);
        notifyFailed();
      }
      if (!isCancelled) {
        throw t;
      }
      throw t;
    } finally {
      // 需要關(guān)閉localFetcher秘狞。
      if (localFetcher != null) {
        localFetcher.cleanup();
      }
      GlideTrace.endSection();
    }
  }

  private void notifyFailed() {
    setNotifiedOrThrow();
    GlideException e = new GlideException("Failed to load resource", new ArrayList<>(throwables));
    callback.onLoadFailed(e);
    onLoadFailed();
  }

上邊說了一大堆,大部分都是處理異常情況的邏輯蹈集,只有 runWrapped(); 才是核心方法烁试。

//初始化過程中runReason 的初始值為RunReason.INITIALIZE;
RunReason 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);
    }
  }
//傳過來的是Stage.INITIALIZE
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);
    }
  }
//此時的stage是Stage.SOURCE
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;
    //此處的currentGenerator為SourceGenerator對象拢肆,在runWrapped() 的 switch方法中賦值的减响。
   //startNext()是接口DataFetcherGenerator中的方法,只能看SourceGenerator實現(xiàn)接口后的具體邏輯
    while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) {
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();

      if (stage == Stage.SOURCE) {
        reschedule();
        return;
      }
    }

跟進(jìn) SourceGenerator實現(xiàn)后的startNext()

@Override
public boolean startNext() {
    //緩存判斷的一些邏輯
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      //放入緩存郭怪,構(gòu)建緩存key支示,存儲原圖等..這里就不展開分析了
      cacheData(data);
    }
    //當(dāng)原始圖片放入磁盤緩存后,sourceCacheGenerator為DataCacheGenerator
    //然后繼續(xù)執(zhí)行DataCacheGenerator的startNext方法
    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    //沒有開啟磁盤緩存或獲取不到磁盤緩存的情況下
    sourceCacheGenerator = null;

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      //此處的helper為DecodeHelper對象鄙才,在DecodeJob.build()->DecodeJob.init()方法中提前進(jìn)行了創(chuàng)建胸完。
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
              || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        //注意后續(xù)loadData對象實例化的部分。
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

  //DecodeHelper.getLoadData() 
  List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear();
      //modelLoaders 得到的是一個HttpGlideUrlLoader 對象列表具體可跟進(jìn)getModelLoaders()方法查看缓苛。同時可以倒推几睛。
      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);
        //相當(dāng)于HttpGlideUrlLoader.buildLoadData();
        LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
        if (current != null) {
          loadData.add(current);
        }
      }
    }
    return loadData;
  }

//HttpGlideUrlLoader 類的buildLoadData 方法
@Override
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height, @NonNull Options options) {
    // glideurl記錄解析的url,因此緩存它們可以節(jié)省一些對象實例化和解析url所花費的時間浓冒。
    GlideUrl url = model;
    if (modelCache != null) {
      url = modelCache.get(model, 0, 0);
      if (url == null) {
        modelCache.put(model, 0, 0, model);
        url = model;
      }
    }
    int timeout = options.get(TIMEOUT);
    //此處為loadData對象實例化
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
  }

public LoadData(@NonNull Key sourceKey, @NonNull DataFetcher<Data> fetcher) {
      this(sourceKey, Collections.<Key>emptyList(), fetcher);
}

public LoadData( Key sourceKey, List<Key> alternateKeys, DataFetcher<Data> fetcher) {
      this.sourceKey = Preconditions.checkNotNull(sourceKey);
      this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
      //這個fetcher類型跟傳入的DataFetcher有關(guān)栽渴,此時傳入的是HttpUrlFetcher。
      this.fetcher = Preconditions.checkNotNull(fetcher);
}


還記得上述的SourceGenerator.startNext()方法中的這一句么稳懒?
loadData.fetcher.loadData(helper.getPriority(), this);
此時的 loadData.fetcher為HttpUrlFetcher闲擦,為啥是HttpUrlFetcher上述的LoadData對象初始化已經(jīng)說明了。

//HttpUrlFetcher.loadData()
@Override
public void loadData(Priority priority, DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
      //核心在這里
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      //此時的callback為SourceGenerator
      //因為調(diào)用loadData()的地方是在SourceGenerator.startNext()loadData.fetcher.loadData(helper.getPriority(), this)中
     //調(diào)用時的入?yún)his則為SourceGenerator對象。
     callback.onDataReady(result);
    } catch (IOException e) {
    ...
      callback.onLoadFailed(e);
    } finally {
    ...
    }
  }

//我們繼續(xù)看下墅冷,快到頭了纯路。
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 {
      //使用.equals比較URL會執(zhí)行額外的網(wǎng)絡(luò)I/O,并且通常會中斷可查看以下博客
      //http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
      try {
        if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
          throw new HttpException("In re-direct loop");
        }
      } catch (URISyntaxException e) {
        // Do nothing, this is best effort.
      }
    }

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

    // Stop the urlConnection instance of HttpUrlConnection from following redirects so that
    // redirects will be handled by recursive calls to this method, loadDataWithRedirects.
    urlConnection.setInstanceFollowRedirects(false);

    // Connect explicitly to avoid errors in decoders if connection fails.
    urlConnection.connect();
    // Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
    stream = urlConnection.getInputStream();
    if (isCancelled) {
      return null;
    }
    final int statusCode = urlConnection.getResponseCode();
    if (isHttpOk(statusCode)) {
      //如果返回的狀態(tài)碼是200則直接返回數(shù)據(jù)流
      return getStreamForSuccessfulRequest(urlConnection);
    } else if (isHttpRedirect(statusCode)) {
      //如果返回的是302重定向俺榆,則獲取重定向地址遞歸請求感昼。
      String redirectUrlString = urlConnection.getHeaderField("Location");
      if (TextUtils.isEmpty(redirectUrlString)) {
        throw new HttpException("Received empty or null redirect url");
      }
      URL redirectUrl = new URL(url, redirectUrlString);
      // Closing the stream specifically is required to avoid leaking ResponseBodys in addition
      // to disconnecting the url connection below. See #2352.
      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);
    }
  }


至此網(wǎng)絡(luò)的加載回調(diào)的流程結(jié)束了,后續(xù)就是數(shù)據(jù)流的處理過程罐脊。
但是 DecodeJob.runWrapped() 整個過程有點亂定嗓,咱們捋一下。

    1. 通過RunReason.INITIALIZE判斷runWrapped()中的switch走第一個case萍桌。
    1. 調(diào)用getNextStage()獲得返回值為Stage.SOURCE
    1. 利用 2的返回值宵溅,調(diào)用 getNextGenerator()初始化SourceGenerator對象
    1. 調(diào)用runGenerators()方法連帶調(diào)用currentGenerator.startNext()上炎,此時的currentGenerator為SourceGenerator對象恃逻。
    1. SourceGenerator.startNext()方法中,調(diào)用DecodeHelper.getLoadData().get(loadDataListIndex++);此處的DecodeHelper即是在DecodeJob.build()->DecodeJob.init()中初始化的DecodeHelper藕施。
    1. DecodeHelper.getLoadData()中獲取到HttpGlideUrlLoader列表對象(通過glideContext.getRegistry().getModelLoaders(model)獲取到)寇损。
    1. 循環(huán)HttpGlideUrlLoader列表,調(diào)用HttpGlideUrlLoader.buildLoadData()初始化LoadData對象裳食。
    1. 此時第5步的 helper.getLoadData().get()邏輯走完矛市,繼續(xù)SourceGenerator.startNext()中的loadData.fetcher.loadData()loadData.fetcher類型依賴于LoadData初始化诲祸,此時的類型是HttpUrlFetcher浊吏,也就是HttpUrlFetcher.loadData();
    1. HttpUrlFetcher.loadData()中調(diào)用loadDataWithRedirects(),然后通過HttpURLConnection請求對應(yīng)的url地址獲取InputStream數(shù)據(jù)流。
    • 9.1請求有兩種情況救氯,第一是直接返回狀態(tài)碼200找田,則直接返回數(shù)據(jù)流。
    • 9.2狀態(tài)碼返回302着憨,通過getHeaderField("Location")獲取重定向的URL遞歸調(diào)用loadDataWithRedirects()進(jìn)行請求墩衙。
    1. 請求完成后,在HttpUrlFetcher.loadData()方法中調(diào)用callback.onDataReady(result);此時的callback是loadData()的入?yún)⑾砣印6?strong>HttpUrlFetcher.loadData()是在之前的SourceGenerator.startNext()中調(diào)用的底桂,并且傳了this,因此callback為SourceGenerator

總結(jié)完后是不是清晰了一些惧眠,上邊這一大堆,最重要的目的就是通過HttpURLConnection獲取到圖片的數(shù)據(jù)流于个,并通過回調(diào)返回氛魁。


不可久留一處,我遵循此道,直至終結(jié)

3.請求成功后的數(shù)據(jù)處理(InputStream的處理)

至此秀存,我們已經(jīng)拿到了網(wǎng)絡(luò)返回的InputStream數(shù)據(jù)流捶码,接下來我們看下返回的數(shù)據(jù)流到底咋用的?

//SourceGenerator.onDataReady()
@Override
public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      // 我們可能會接到其他回調(diào)或链。在此之前惫恼,我們應(yīng)該重新切回Glide的線程。
      //cb是一個FetcherReadyCallback對象澳盐,在SourceGenerator構(gòu)造函數(shù)賦值祈纯,初始化在DecodeJob類中初始化,且傳了this叼耙,因此cb為DecodeJob腕窥。
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(loadData.sourceKey, data,loadData.fetcher, loadData.fetcher.getDataSource(), originalKey);
    }
  }

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

//DecodeJob.onDataFetcherReady()
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
  //賦值給全局變量,注意data為數(shù)據(jù)流
    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();
      }
    }
  }

//DecodeJob.decodeFromRetrievedData()
private void decodeFromRetrievedData() {
...
    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();
    }
  }

//DecodeJob.decodeFromData()
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException {
    try {
      ...
      long startTime = LogTime.getLogTime();
      Resource<R> result = decodeFromFetcher(data, dataSource);
      ...
      return result;
    } finally {
      fetcher.cleanup();
    }
  }

//DecodeJob.decodeFromFetcher()
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
      throws GlideException {
    LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
    return runLoadPath(data, dataSource, path);
  }

我們清楚的知道了數(shù)據(jù)流返回后通過cb.onDataFetcherReady()回調(diào)到了DecodeJob類中筛婉。
然后他們的調(diào)用鏈?zhǔn)沁@樣的簇爆。
DecodeJob.onDataFetcherReady()
->DecodeJob.decodeFromRetrievedData()
-->DecodeJob.decodeFromData()
--->DecodeJob.decodeFromFetcher()
---->DecodeHelper.getLoadPath((Class<Data>) data.getClass())

fu*k......怎么又調(diào)用到DecodeHelper中去了?目前從DecodeJob中還啥也看不出來...

向Glide開炮.....

DecodeHelper.getLoadPath()

  <Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
    return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
  }

//Registry.getLoadPath()
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(Class<Data> dataClass, Class<TResource> resourceClass, Class<Transcode> transcodeClass) {
    LoadPath<Data, TResource, Transcode> result = loadPathCache.get(dataClass, resourceClass, transcodeClass);
    if (loadPathCache.isEmptyLoadPath(result)) {
      return null;
    } else if (result == null) {
      List<DecodePath<Data, TResource, Transcode>> decodePaths = getDecodePaths(dataClass, resourceClass, transcodeClass);
      // It's possible there is no way to decode or transcode to the desired types from a given
      // data class.
      if (decodePaths.isEmpty()) {
        result = null;
      } else {
        //利用上述創(chuàng)建的decodePaths 作為參數(shù)創(chuàng)建LoadPath對象并添加到loadPathCache對象中
        result = new LoadPath<>(dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
      }
      loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
    }
    return result;
  }

//Registry.getDecodePaths()
@NonNull
private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
       Class<Data> dataClass, Class<TResource> resourceClass, Class<Transcode> transcodeClass) {
    List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
    List<Class<TResource>> registeredResourceClasses = decoderRegistry.getResourceClasses(dataClass, resourceClass);

    for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
      List<Class<Transcode>> registeredTranscodeClasses = transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);

      for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {
        List<ResourceDecoder<Data, TResource>> decoders = decoderRegistry.getDecoders(dataClass, registeredResourceClass);
        ResourceTranscoder<TResource, Transcode> transcoder = transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
        @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
        DecodePath<Data, TResource, Transcode> path =
            new DecodePath<>(dataClass,registeredResourceClass,registeredTranscodeClass,decoders,transcoder, throwableListPool);
        decodePaths.add(path);
      }
    }
    return decodePaths;
  }

我們還得梳理下

    1. 先通過DecodeHelper.getLoadPath()調(diào)用Registry.getLoadPath()然后調(diào)用Registry.getDecodePaths()爽撒。
    1. 循環(huán)并調(diào)用getDecoders()得到解碼器ResourceDecoder列表入蛆。
    1. 創(chuàng)建DecodePath對象,并將ResourceDecoder列表對象傳入進(jìn)去硕勿。這個DecodePath就是用來解碼和轉(zhuǎn)換的實體類對象哨毁。

榮耀存于心,而非留于形首尼,汝之赴死挑庶,易如反掌

4.數(shù)據(jù)解碼

上述的數(shù)據(jù)流經(jīng)歷了一系列的準(zhǔn)備,終于要解碼了软能。
上述的DecodeJob.decodeFromFetcher()中還有一個方法叫runLoadPath(data, dataSource, path);

//DecodeJob.runLoadPath()
private <Data, ResourceType> Resource<R> runLoadPath(
      Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path)throws GlideException {
    Options options = getOptionsWithHardwareConfig(dataSource);
    DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
    try {
      // ResourceType in DecodeCallback below is required for compilation to work with gradle.
      return path.load(rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
    } finally {
      rewinder.cleanup();
    }
  }

//LoadPath.load()
public Resource<Transcode> load(DataRewinder<Data> rewinder,Options options, int width, int height,
      DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
    List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());
    try {
      return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
    } finally {
      listPool.release(throwables);
    }
  }

//LoadPath.loadWithExceptionList()
private Resource<Transcode> loadWithExceptionList(
      DataRewinder<Data> rewinder,
      @NonNull Options options,
      int width,
      int height,
      DecodePath.DecodeCallback<ResourceType> decodeCallback,
      List<Throwable> exceptions)
      throws GlideException {
    Resource<Transcode> result = null;
    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = decodePaths.size(); i < size; i++) {
      //循環(huán)拿到DecodePath對象
      DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
      try {
        //調(diào)用DecodePath.decode()方法
        result = path.decode(rewinder, width, height, options, decodeCallback);
      } catch (GlideException e) {
        exceptions.add(e);
      }
      if (result != null) {
        break;
      }
    }

    if (result == null) {
      throw new GlideException(failureMessage, new ArrayList<>(exceptions));
    }

    return result;
  }

那種勝利在望的感覺終于出現(xiàn)了....

//DecodePath.decode()
public Resource<Transcode> decode(
      DataRewinder<DataType> rewinder,int width,int height, Options options,
      DecodeCallback<ResourceType> callback)throws GlideException {
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    return transcoder.transcode(transformed, options);
  }

@NonNull
private Resource<ResourceType> decodeResource(
      DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options)throws GlideException {
    List<Throwable> exceptions = Preconditions.checkNotNull(listPool.acquire());
    try {
      return decodeResourceWithList(rewinder, width, height, options, exceptions);
    } finally {
      listPool.release(exceptions);
    }
  }

@NonNull
private Resource<ResourceType> decodeResourceWithList(
      DataRewinder<DataType> rewinder,int width, int height,Options options,List<Throwable> exceptions)throws GlideException {
    Resource<ResourceType> result = null;
    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = decoders.size(); i < size; i++) {
      //獲取解碼器迎捺,此時的 ResourceDecoder為StreamBitmapDecoder
      ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
      try {
        DataType data = rewinder.rewindAndGet();
        if (decoder.handles(data, options)) {
          data = rewinder.rewindAndGet();
          //等同于調(diào)用StreamBitmapDecoder.decode()
          result = decoder.decode(data, width, height, options);
        }
        // Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but
        // instead log and continue. See #2406 for an example.
      } catch (IOException | RuntimeException | OutOfMemoryError e) {
       ...
    }

    ...
    return result;
  }

//StreamBitmapDecoder.decode()
@Override
//此時的source正式我們請求網(wǎng)絡(luò)回來的InputStream數(shù)據(jù)流
public Resource<Bitmap> decode(InputStream source, int width, int height,  Options options) throws IOException {

    // 用于修復(fù)標(biāo)記限制,以避免分配適合整個圖像的緩沖區(qū)查排。
    final RecyclableBufferedInputStream bufferedStream;
    final boolean ownsBufferedStream;
    if (source instanceof RecyclableBufferedInputStream) {
      bufferedStream = (RecyclableBufferedInputStream) source;
      ownsBufferedStream = false;
    } else {
      bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
      ownsBufferedStream = true;
    }

    // 用于檢索讀取時引發(fā)的異常凳枝。
    //TODO(#126):當(dāng)框架不再返回部分解碼的位圖或提供要確定位圖是否已部分解碼,請考慮刪除跋核。
    ExceptionCatchingInputStream exceptionStream =ExceptionCatchingInputStream.obtain(bufferedStream);

    //用于讀取數(shù)據(jù)岖瑰。
    //確保在讀取圖像標(biāo)題后始終可以重置,以便嘗試解碼完整圖像砂代,即使頭解碼失敗和/或溢出我們的讀取緩沖區(qū)
    MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
    UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
    try {
      return downsampler.decode(invalidatingStream, width, height, options, callbacks);
    } finally {
      exceptionStream.release();
      if (ownsBufferedStream) {
        bufferedStream.release();
      }
    }
  }

我以為StreamBitmapDecoder終于要解碼了蹋订,可Glide還是比較執(zhí)著。
StreamBitmapDecoder只是將source進(jìn)行了包裝刻伊,包裝成了RecyclableBufferedInputStream對象
最后調(diào)用了 downsampler.decode(invalidatingStream, width, height, options, callbacks);

來 ...

//Downsampler.decode()
public Resource<Bitmap> decode(
      InputStream is,
      int requestedWidth,
      int requestedHeight,
      Options options,
      DecodeCallbacks callbacks)
      throws IOException {
    ...

    byte[] bytesForOptions = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
    BitmapFactory.Options bitmapFactoryOptions = getDefaultOptions();
    bitmapFactoryOptions.inTempStorage = bytesForOptions;

    DecodeFormat decodeFormat = options.get(DECODE_FORMAT);
    PreferredColorSpace preferredColorSpace = options.get(PREFERRED_COLOR_SPACE);
    DownsampleStrategy downsampleStrategy = options.get(DownsampleStrategy.OPTION);
    boolean fixBitmapToRequestedDimensions = options.get(FIX_BITMAP_SIZE_TO_REQUESTED_DIMENSIONS);
    boolean isHardwareConfigAllowed =
        options.get(ALLOW_HARDWARE_CONFIG) != null && options.get(ALLOW_HARDWARE_CONFIG);

    try {
      Bitmap result =
          decodeFromWrappedStreams(
              is,
              bitmapFactoryOptions,
              downsampleStrategy,
              decodeFormat,
              preferredColorSpace,
              isHardwareConfigAllowed,
              requestedWidth,
              requestedHeight,
              fixBitmapToRequestedDimensions,
              callbacks);
      return BitmapResource.obtain(result, bitmapPool);
    } finally {
      releaseOptions(bitmapFactoryOptions);
      byteArrayPool.put(bytesForOptions);
    }
  }

//Downsampler.decodeFromWrappedStreams()
private Bitmap decodeFromWrappedStreams(
      InputStream is,
      BitmapFactory.Options options,
      DownsampleStrategy downsampleStrategy,
      DecodeFormat decodeFormat,
      PreferredColorSpace preferredColorSpace,
      boolean isHardwareConfigAllowed,
      int requestedWidth,
      int requestedHeight,
      boolean fixBitmapToRequestedDimensions,
      DecodeCallbacks callbacks)
      throws IOException {
    ...

    Bitmap downsampled = decodeStream(is, options, callbacks, bitmapPool);
    callbacks.onDecodeComplete(bitmapPool, downsampled);

    ...
    Bitmap rotated = null;
    if (downsampled != null) {
      // 如果我們縮放露戒,位圖密度將是我們的目標(biāo)密度椒功。在這里我們把它修正回預(yù)期的密度dpi。
      downsampled.setDensity(displayMetrics.densityDpi);

      rotated = TransformationUtils.rotateImageExif(bitmapPool, downsampled, orientation);
      if (!downsampled.equals(rotated)) {
        bitmapPool.put(downsampled);
      }
    }

    return rotated;
  }

//Downsampler.decodeStream()
private static Bitmap decodeStream(
      InputStream is,
      BitmapFactory.Options options,
      DecodeCallbacks callbacks,
      BitmapPool bitmapPool)
      throws IOException {
    if (options.inJustDecodeBounds) {
      is.mark(MARK_POSITION);
    } else {
      // Once we've read the image header, we no longer need to allow the buffer to expand in
      // size. To avoid unnecessary allocations reading image data, we fix the mark limit so that it
      // is no larger than our current buffer size here. We need to do so immediately before
      // decoding the full image to avoid having our mark limit overridden by other calls to
      // mark and reset. See issue #225.
      callbacks.onObtainBounds();
    }
    // BitmapFactory.Options out* variables are reset by most calls to decodeStream, successful or
    // otherwise, so capture here in case we log below.
    int sourceWidth = options.outWidth;
    int sourceHeight = options.outHeight;
    String outMimeType = options.outMimeType;
    final Bitmap result;
    TransformationUtils.getBitmapDrawableLock().lock();
    try {
      result = BitmapFactory.decodeStream(is, null, options);
    } catch (IllegalArgumentException e) {
      IOException bitmapAssertionException =
          newIoExceptionForInBitmapAssertion(e, sourceWidth, sourceHeight, outMimeType, options);
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(
            TAG,
            "Failed to decode with inBitmap, trying again without Bitmap re-use",
            bitmapAssertionException);
      }
      if (options.inBitmap != null) {
        try {
          is.reset();
          bitmapPool.put(options.inBitmap);
          options.inBitmap = null;
          return decodeStream(is, options, callbacks, bitmapPool);
        } catch (IOException resetException) {
          throw bitmapAssertionException;
        }
      }
      throw bitmapAssertionException;
    } finally {
      TransformationUtils.getBitmapDrawableLock().unlock();
    }

    if (options.inJustDecodeBounds) {
      is.reset();
    }
    return result;
  }

我**智什,終于看到 BitmapFactory.decodeStream() 了动漾,太難了。荠锭。旱眯。
我們還是簡單回顧下

    1. Downsampler.decode()連帶調(diào)用了Downsampler.decodeFromWrappedStreams()
    1. 然后調(diào)用了Downsampler.decodeStream()并在該方法中調(diào)用了BitmapFactory.decodeStream()
    1. 到目前為止數(shù)據(jù)流已經(jīng)解碼完成轉(zhuǎn)換為Bitmap對象。

不要畏懼迷離之道证九,傳統(tǒng)是智慧的糟粕

5.數(shù)據(jù)轉(zhuǎn)換

上述邏輯中已經(jīng)將數(shù)據(jù)流對象轉(zhuǎn)換為了Bitmap對象删豺。但是DecodePath.decode()完成后還需要 transcoder.transcode(transformed, options);
此時的transcoder為BitmapDrawableTranscoder.transcode()

//BitmapDrawableTranscoder.transcode()
public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode, @NonNull Options options) {
    return LazyBitmapDrawableResource.obtain(resources, toTranscode);
  }

//BitmapDrawableTranscoder.transcode()
@Nullable
public static Resource<BitmapDrawable> obtain(Resources resources, Resource<Bitmap> bitmapResource) {
    if (bitmapResource == null) {
      return null;
    }
    return new LazyBitmapDrawableResource(resources, bitmapResource);
}

到這里通過 DecodePath.decode()先獲取到了Bitmap對象。
然后通過transcoder.transcode()將Bitmap包裝成了LazyBitmapDrawableResource對象甫贯。


斷劍重鑄之日吼鳞,騎士歸來之時

6.圖片顯示與那一堆回調(diào)
  • 圖片數(shù)據(jù)流獲取到了
  • 圖片數(shù)據(jù)流解碼為了Bitmap對象
  • 圖片對象轉(zhuǎn)換為了LazyBitmapDrawableResource對象

然后呢?.....該從哪再開始呢叫搁?還沒顯示到頁面呢...

你還記得DecodeJob.decodeFromRetrievedData()方法嗎赔桌?
不記得可以往上翻一下,為了方便渴逻,這里我再復(fù)制一份過來疾党。

//DecodeJob.decodeFromRetrievedData()
private void decodeFromRetrievedData() {
...
    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();
    }
  }

其實我們上邊分析的這些大部分都是DecodeJob.decodeFromData()旗下的子邏輯。
還有一個方法是 DecodeJob.notifyEncodeAndRelease(resource, currentDataSource);

//DecodeJob.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 {
      //是否可以將轉(zhuǎn)換的圖片緩存
      if (deferredEncodeManager.hasResourceToEncode()) {
        //磁盤緩存的入口
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      if (lockedResource != null) {
        lockedResource.unlock();
      }
    }
    // Call onEncodeComplete outside the finally block so that it's not called if the encode process
    // throws.
    onEncodeComplete();
  }

//DecodeJob.notifyComplete()
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
     //此時的callback為EngineJob惨奕,因為等同于調(diào)用EngineJob.onResourceReady()
    callback.onResourceReady(resource, dataSource);
  }

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


//EngineJob.notifyCallbacksOfResult()
void notifyCallbacksOfResult() {
    ResourceCallbacksAndExecutors copy;
    Key localKey;
    EngineResource<?> localResource;
    synchronized (this) {
      stateVerifier.throwIfRecycled();
      if (isCancelled) {
        // TODO: 回收并加入緩存雪位。
        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對象,并沒有其他處理
      engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener);
      // 在下面的回調(diào)期間保留資源梨撞,這樣我們就不會在通知資源是否由其中一個回調(diào)同步釋放時對其進(jìn)行回收雹洗。
      //在這里用一個鎖獲取它,這樣在下面的下一個鎖定部分之前執(zhí)行的任何新添加的回調(diào)在我們調(diào)用回調(diào)之前都不能回收資源卧波。
      hasResource = true;
      copy = cbs.copy();
      incrementPendingCallbacks(copy.size() + 1);

      localKey = key;
      localResource = engineResource;
    }
     //注意 這個engineJobListener實際上是Engine對象
    //內(nèi)部緩存存儲的入口时肿,等同于調(diào)用Engine.onEngineJobComplete()
    engineJobListener.onEngineJobComplete(this, localKey, localResource);

    for (final ResourceCallbackAndExecutor entry : copy) {
      //切主線程顯示圖片
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
    //通知上層刪除弱引用緩存數(shù)據(jù)
    decrementPendingCallbacks();
  }

//Engine.onEngineJobComplete()
@Override
public synchronized void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    // 如果開啟內(nèi)存緩存的話,將解析后的圖片添加到弱引用緩存港粱。
    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);
    //看這里將資源添加到弱引用緩存中螃成。
     //key值不重復(fù)返回null,key值重復(fù)返回舊對象
    ResourceWeakReference removed = activeEngineResources.put(key, toPut);
    if (removed != null) {
      //如果key值重復(fù),就將之前的弱引用對象的圖片資源置為null  
      removed.reset();
    }
  }

到這該罵娘了查坪,沒事寸宏,該罵罵,罵完咱們繼續(xù).
DecodeJob.notifyEncodeAndRelease(resource, currentDataSource);都看完了到底哪里顯示的圖片呢偿曙?
能不能給我來個痛快?

我們注意EngineJob.notifyCallbacksOfResult()最后有個for循環(huán)
循環(huán)調(diào)用ResourceCallbackAndExecutor.executor.execute(new CallResourceReady(entry.cb))
其實ResourceCallbackAndExecutor.executor是一個線程池
CallResourceReady是個Runnable氮凝,執(zhí)行的正是這個Runnable中的run方法。

在這個CallResourceReady對象中就是圖片顯示的處理邏輯望忆,大哥們我們還得跟進(jìn)...

private class CallResourceReady implements Runnable {

    private final ResourceCallback cb;

    CallResourceReady(ResourceCallback cb) {
      this.cb = cb;
    }

    @Override
    public void run() {
      // Make sure we always acquire the request lock, then the EngineJob lock to avoid deadlock
      // (b/136032534).
      synchronized (cb.getLock()) {
        synchronized (EngineJob.this) {
          if (cbs.contains(cb)) {
            // Acquire for this particular callback.
            engineResource.acquire();
            callCallbackOnResourceReady(cb);
            removeCallback(cb);
          }
          decrementPendingCallbacks();
        }
      }
    }
  }

//EngineJob.callCallbackOnResourceReady()
void callCallbackOnResourceReady(ResourceCallback cb) {
    try {
      // This is overly broad, some Glide code is actually called here, but it's much
      // simpler to encapsulate here than to do so at the actual call point in the
      // Request implementation.
      //翻山越嶺終于見到你覆醇,這個cb就是SingleRequest
      //cb最初是通過SingleRequest.onSizeReady()中的engine.load()傳進(jìn)來的.
      //調(diào)用時候傳的是this朵纷,因此此處等于同調(diào)用SingleRequest.onResourceReady()
      cb.onResourceReady(engineResource, dataSource);
    } catch (Throwable t) {
      throw new CallbackException(t);
    }
  }


/** A callback method that should never be invoked directly. */
    //SingleRequest.onResourceReady()
  @SuppressWarnings("unchecked")
  @Override
  public void onResourceReady(Resource<?> resource, DataSource dataSource) {
    stateVerifier.throwIfRecycled();
    Resource<?> toRelease = null;
    try {
      synchronized (requestLock) {
        loadStatus = null;
         ....異常處理

        if (!canSetResource()) {
          toRelease = resource;
          this.resource = null;
          //在請求canSetResource()之前炭臭,不能將狀態(tài)設(shè)置為完成永脓。
          status = Status.COMPLETE;
          return;
        }

        onResourceReady((Resource<R>) resource, (R) received, dataSource);
      }
    } finally {
      if (toRelease != null) {
        engine.release(toRelease);
      }
    }
  }

  //SingleRequest.onResourceReady()
@GuardedBy("requestLock")
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;

    isCallingCallbacks = true;
    try {
      boolean anyListenerHandledUpdatingTarget = false;
      if (requestListeners != null) {
        for (RequestListener<R> listener : requestListeners) {
          anyListenerHandledUpdatingTarget |=
              //核心1
              listener.onResourceReady(result, model, target, dataSource, isFirstResource);

        }
      }
      anyListenerHandledUpdatingTarget |=
          targetListener != null
              //核心2
              && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);

      if (!anyListenerHandledUpdatingTarget) {
        Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
        //核心3
        target.onResourceReady(result, animation);
      }
    } finally {
      isCallingCallbacks = false;
    }
    //核心4
    notifyLoadSuccess();
  }

以上邏輯中我們寫了4個注釋,分別和核心1234鞋仍,我們一個一個看下
核心1和核心2

 listener.onResourceReady(result, model, target, dataSource, isFirstResource);
 targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);  

其實這個方法往下跟蹤是一個native方法常摧,作用是用來喚醒此對象監(jiān)視器上等待的所有線程。

核心3

 //此時的target為ImageViewTarget
target.onResourceReady(result, animation);

//ImageViewTarget.onResourceReady()
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      maybeUpdateAnimatable(resource);
    }
  }

//ImageViewTarget.setResourceInternal()
private void setResourceInternal(@Nullable Z resource) {
    // Order matters here. Set the resource first to make sure that the Drawable has a valid and
    // non-null Callback before starting it.
    setResource(resource);
    //設(shè)置動畫的
    maybeUpdateAnimatable(resource);
  }
//終點站到了威创。
protected void setResource(Bitmap resource) {
    view.setImageBitmap(resource);
  }

看到了么落午,一切的一切都在這句view.setImageBitmap(resource);后結(jié)束了。

我們在看下核心4

notifyLoadSuccess();

private void notifyLoadSuccess() {
    if (requestCoordinator != null) {
      requestCoordinator.onRequestSuccess(this);
    }
  }
//主要做清理工作肚豺。
public void onRequestSuccess(Request request) {
    synchronized (requestLock) {
      if (request.equals(thumb)) {
        thumbState = RequestState.SUCCESS;
        return;
      }
      fullState = RequestState.SUCCESS;
      if (parent != null) {
        parent.onRequestSuccess(this);
      }
      // Clearing the thumb is not necessarily safe if the thumb is being displayed in the Target,
      // as a layer in a cross fade for example. The only way we know the thumb is not being
      // displayed and is therefore safe to clear is if the thumb request has not yet completed.
      if (!thumbState.isComplete()) {
        thumb.clear();
      }
    }
  }


永遠(yuǎn)不要忘記溃斋,吾等為何而戰(zhàn)..

7. 緩存的維護(hù)與流程

但是還有一個疑問沒有解決,那網(wǎng)絡(luò)請求后的數(shù)據(jù)到底緩存到哪里了吸申?
我們先還得看下SingleRequest.onResourceReady()的最后梗劫,在finally中有句 engine.release(toRelease);

//Engine.release(toRelease)
public void release(Resource<?> resource) {
    if (resource instanceof EngineResource) {
      ((EngineResource<?>) resource).release();
    } else {
      throw new IllegalArgumentException("Cannot release anything but an EngineResource");
    }
  }
/**
*減少使用包裝資源的使用者數(shù)〗夭辏總機(jī)上必須呼叫線梳侨。
*只有當(dāng)調(diào)用{@link\acquire()}方法的使用者資源用完了
*一般來說,外部用戶不應(yīng)該調(diào)用這個方法框架會幫你處理的日丹。
*/
//EngineResource.release()
void release() {
    boolean release = false;
    synchronized (this) {
      if (acquired <= 0) {
        throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
      }
      if (--acquired == 0) {
        release = true;
      }
    }
    if (release) {
      listener.onResourceReleased(key, this);
    }
  }

//Engine.onResourceReleased()
@Override
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    //刪除弱引用緩存
    activeResources.deactivate(cacheKey);
    if (resource.isMemoryCacheable()) {
      //看到了么走哺,這里加入了緩存,這cache即是LruResourceCache
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource);
    }
  }

總結(jié)哲虾,緩存的入口主要體現(xiàn)在3個地方丙躏。

    1. 內(nèi)部緩存存儲的入口,等同于調(diào)用Engine.onEngineJobComplete()
      EngineJob.notifyCallbacksOfResult()
      -> engineJobListener.onEngineJobComplete(this, localKey, localResource);
    1. 如果開啟內(nèi)存緩存的話束凑,將解析后的圖片添加到弱引用緩存,添加前先封裝成ResourceWeakReference對象晒旅,如果key有重復(fù)的則將之前的弱引用列表的中對應(yīng)的數(shù)據(jù)制空。
      Engine.onEngineJobComplete()
      -> activeResources.activate(key, resource);
    1. 磁盤緩存的入口
      DecodeJob.notifyEncodeAndRelease()
      ->deferredEncodeManager.encode(diskCacheProvider, options);

如果LruCache有不懂得可以看我的另一個系列的文章 LruCache緩存機(jī)制湘今,深入淺出敢朱,發(fā)現(xiàn)了一個源碼bug

我們最后看下緩存的整體使用流程圖


內(nèi)存緩存流程圖.png

磁盤緩存的流程圖.png

至此,完整的兩篇Glide講解已經(jīng)完結(jié)摩瞎,看源碼的過程很痛苦拴签,但分析清整個脈絡(luò)很清奇。
我們的日常工作大都是做著重復(fù)的勞動旗们,成長的機(jī)會少之又少蚓哩,無輪什么時候也不要忘了那顆赤誠學(xué)習(xí)的心。
如果本文給你帶來了一點點幫助麻煩給個贊鼓勵一下上渴,同時如果文中有任何錯誤歡迎指出留言岸梨。
謝謝大家...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末喜颁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子曹阔,更是在濱河造成了極大的恐慌半开,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赃份,死亡現(xiàn)場離奇詭異寂拆,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)抓韩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門纠永,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谒拴,你說我怎么就攤上這事尝江。” “怎么了英上?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵炭序,是天一觀的道長。 經(jīng)常有香客問我善延,道長少态,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任易遣,我火速辦了婚禮彼妻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘豆茫。我一直安慰自己侨歉,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布揩魂。 她就那樣靜靜地躺著幽邓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪火脉。 梳的紋絲不亂的頭發(fā)上牵舵,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音倦挂,去河邊找鬼畸颅。 笑死,一個胖子當(dāng)著我的面吹牛方援,可吹牛的內(nèi)容都是我干的没炒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼犯戏,長吁一口氣:“原來是場噩夢啊……” “哼送火!你這毒婦竟也來了拳话?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤种吸,失蹤者是張志新(化名)和其女友劉穎弃衍,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體骨稿,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡笨鸡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了坦冠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡哥桥,死狀恐怖辙浑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拟糕,我是刑警寧澤判呕,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站送滞,受9級特大地震影響侠草,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜犁嗅,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一边涕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧褂微,春花似錦功蜓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至求厕,卻和暖如春著隆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呀癣。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工美浦, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人十艾。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓抵代,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忘嫉。 傳聞我的和親對象是個殘疾皇子荤牍,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容