Glide原始碼解析四(解碼和轉碼)

妖久發表於2024-03-03

本文基於Glide 4.11.0

Glide載入過程有一個解碼過程,比如將url載入為inputStream後,要將inputStream解碼為Bitmap。

 

Glide原始碼解析一我們大致知道了Glide載入的過程,所以我們可以直接從這裡看起,在這個過程中我們以從檔案中載入bitmap為例:

DecodeJob的一個方法:

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

主要是這個方法:resource = decodeFromData(currentFetcher, currentData, currentDataSource);

這時候currentData為FileInputStream,因為我們載入的是本地檔案。 

currentDateSource為LOCAL,即為本地的資源

 

 

 我們繼續找下去

resource = decodeFromData(currentFetcher, currentData, currentDataSource);

----------------->

Resource<R> result = decodeFromFetcher(data, dataSource);

------------------>

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

這裡獲取到LoadPath的物件,我麼先看看LoadPath有什麼?

我們可以看到一個DecodePaths:

 

 DecodePath裡面又儲存著decoders

 

decoders便是我們需要的解碼器,拿到解碼器後就可以進行解碼了。

那怎麼拿到?

Glide原始碼解析三中我們知道這些解碼器都註冊在Register中,所以我們也是要透過它來拿:

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

---------------->

 

@Nullable
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
    @NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,
    @NonNull 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 {
      result =
          new LoadPath<>(
              dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
    }
    loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
  }
  return result;
}

首先會先從快取中拿,快取中拿不到再透過下面的方法去拿:

List<DecodePath<Data, TResource, Transcode>> decodePaths = getDecodePaths(dataClass, resourceClass, transcodeClass);

 

private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
    @NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,
    @NonNull 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;
}

 該方法各個引數如下:

dataClass為InputStream,這是被解碼的物件

 

resourceClass為Object,要解碼成為Object

 

 

transcodeClass為Drawable,要轉碼為Drawable

我們看這個方法:

decoderRegistry.getResourceClasses:

 

public synchronized <T, R> List<Class<R>> getResourceClasses(@NonNull Class<T> dataClass,
    @NonNull Class<R> resourceClass) {
  List<Class<R>> result = new ArrayList<>();
  for (String bucket : bucketPriorityList) {
    List<Entry<?, ?>> entries = decoders.get(bucket);
    if (entries == null) {
      continue;
    }
    for (Entry<?, ?> entry : entries) {
      if (entry.handles(dataClass, resourceClass)
          && !result.contains((Class<R>) entry.resourceClass)) {
        result.add((Class<R>) entry.resourceClass);
      }
    }
  }
  return result;
}

該方法是為了獲取解碼器中的resourceClass,即解碼後的資源型別。

我們可以看到decoder這個map裡面的內容:

 

各種型別對應的解碼器。

 

只有滿足entry.handles(dataClass, resourceClass),才能被新增返回:

public boolean handles(@NonNull Class<?> dataClass, @NonNull Class<?> resourceClass) {
  return this.dataClass.isAssignableFrom(dataClass) && resourceClass
      .isAssignableFrom(this.resourceClass);
}

由於我們的resourceClass是Object,因此resourceClass .isAssignableFrom(this.resourceClass)總是成立的,所以就看:this.dataClass.isAssignableFrom(dataClass)

而我們的dataClass是InputStream,開啟各種型別,可以看到哪些的dataClass是InputStream:

 

上面框錯了,應該框resourceClass,另外FrameSequenceDrawable是我自定義後註冊進去的,所以Glide原生的是沒有的。

所以最終返回的resource為:

 

 

接下來是針對每一種resourceClass獲取對應的轉碼類(要轉成的物件):

public synchronized <Z, R> List<Class<R>> getTranscodeClasses(
    @NonNull Class<Z> resourceClass, @NonNull Class<R> transcodeClass) {
  List<Class<R>> transcodeClasses = new ArrayList<>();
  // GifDrawable -> Drawable is just the UnitTranscoder, as is GifDrawable -> GifDrawable.
  if (transcodeClass.isAssignableFrom(resourceClass)) {
    transcodeClasses.add(transcodeClass);
    return transcodeClasses;
  }
  for (Entry<?, ?> entry : transcoders) {
    if (entry.handles(resourceClass, transcodeClass)) {
      transcodeClasses.add(transcodeClass);
    }
  }
  return transcodeClasses;
}

如果transcodeClass是resourceClass的父類那就直接返回。

第一個GifDrawable,返回的registeredTranscodeClasses為:

 

 

然後根據dataClass, registeredResourceClass獲取decoders:

 

 

然後根據registeredResourceClass和registeredTranscodeClass獲取transcoder

 

上面具體的獲取過程是類似的,就不過多分析了。

 

然後構造DecodePath,放進下面的集合裡面:

List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();

 

迴圈獲取之後,最終得到的decodePaths如下:

 

 

大致流程:

1、先根據傳進來的resourceClass獲取登入檔中所有註冊的resourceClass得到List<Class<TResource>> registeredResourceClasses

2、兩層for迴圈:

   (1)外層:根據registeredResourceClasses獲取轉碼的class :List<Class<Transcode>> registeredTranscodeClasses

   (2)內層:

            a、根據資源resourceClass獲取所有的解碼器。

            b、根據資源resourceClass和轉碼transcodeClass獲取所有的轉碼器。

            c、構造DecodePath,放進集合裡面。

 

最後得到的List<DecodePath<Data, TResource, Transcode>> decodePaths被放到LoadPath物件裡面(上一層方法可看到)

 

 我們又回到DecodeJob中的方法:

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

獲取到LoadPath後接下來就是要開始執行了runLoadPath了。

 

找下去可以看到該方法:

return path.load(
          rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));

該方法屬於LoadPath物件。

 

層層追溯後,最終來到下面的方法:

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++) {
    DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
    try {
      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;
}

該方法在LoadPath裡面,遍歷decodePaths(這是我們之前獲取後放在LoadPath中的)進行解碼:

result = path.decode(rewinder, width, height, options, decodeCallback);

然後來到:

public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
    @NonNull 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);
}

我們這裡需要看的就是:decodeResource:

最終來到DecodePath裡面的方法:

@NonNull
private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width,
    int height, @NonNull 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<DataType, ResourceType> decoder = decoders.get(i);
    try {
      DataType data = rewinder.rewindAndGet();
      if (decoder.handles(data, options)) {
        data = rewinder.rewindAndGet();
        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) {
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Failed to decode data for " + decoder, e);
      }
      exceptions.add(e);
    }
    if (result != null) {
      break;
    }
  }
  if (result == null) {
    throw new GlideException(failureMessage, new ArrayList<>(exceptions));
  }
  return result;
}

這個方法:decoder.handles(data, options)是判斷該解碼器是否可以對該資源進行解碼,這個方法寫在每個解碼器裡面。

DataRewinder裡面放著需要進行解碼的資料。

解碼後將資源返回。

 

又回到這個方法:

public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
    @NonNull 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);
}

這一句Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);

是對資源進行變換處理,比如圖片的縮放,剪裁等等,這個功能單獨拎出來講。

接下來便是運用轉碼器進行資源的轉碼:

transcoder.transcode(transformed, options)

 

到此就結束了。

 

轉載請標明:https://www.cnblogs.com/tangZH/p/12912698.html

相關文章