Glide源碼解析四(解碼和轉碼)

来源:https://www.cnblogs.com/tangZH/p/12912698.html
-Advertisement-
Play Games

本文基於Glide 4.11.0 Glide載入過程有一個解碼過程,比如將url載入為inputStream後,要將inputStream解碼為Bitmap。 從Glide源碼解析一我們大致知道了Glide載入的過程,所以我們可以直接從這裡看起,在這個過程中我們以從文件中載入bitmap為例: De ...


本文基於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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • CloudCanal 最近對於全周期數據流動進行了初步探索,打通了 Hive 目標端的實時同步,為實時數倉的構建提供了支持,這篇文章簡要做下分享。 ...
  • 本操作在虛擬機上 安裝Redis 1)更新系統 sudo apt update sudo apt upgrade 2)安裝Redis sudo apt install redis-server 3)測試Redis是否工作 redis-cli --version systemctl status re ...
  • 指標是什麼? 業務發展過程中,企業內外部都會產生很多的業務數據,對這些數據進行採集、計算、落庫、分析後,形成的統計結果稱為指標。簡單來說,指標是業務被拆解、量化後形成的數量特征,企業利用數據指標對業務進行精準的號脈,實現對業務的科學管理和有效優化。 在我們對多家企業展開深入調研的過程中,發現數據指標 ...
  • 2024年2月27日,在“2024年世界移動通信大會”(Mobile World Congress 2024,簡稱MWC 2024)上,以“雲原生×AI,躍遷新機遇”為主題的創原會圓桌成功舉辦。會上,全球企業技術精英面對面交流,圍繞雲原生×AI技術變革,分享企業在架構、算力、存儲、數智、應用開發、媒 ...
  • 在大數據處理領域,Apache SeaTunnel 已成為一款備受青睞的開源數據集成平臺,它不僅可以基於Apache Spark和Flink,而且還有社區單獨開發專屬數據集成的Zeta引擎,提供了強大的數據處理能力。隨著SeaTunnel Web的推出,用戶界面(UI)操作變得更加友好,項目部署和管 ...
  • 當用戶需要的計算或者存儲資源冗餘超出業務需求時,可在管理控制台對已有集群進行縮容操作,以便充分利用GaussDB(DWS) 提供的計算資源和存儲資源。 ...
  • Android 修改系統息屏時間. 本篇文章主要記錄下android 如何修改手機息屏時間. 目前手機屏幕超時的時間範圍一般是: 15秒 30秒 1分鐘 2分鐘 5分鐘 10分鐘 30分鐘 那如何設置超過30分鐘呢? 代碼很簡單,如下: private void changeScreenOffTim ...
  • 兩個常用的組件:Material和Scaffold修飾App和H5一樣很固定。 1.Container 2.Text 3.picture import 'package:flutter/material.dart'; void main() { runApp(MaterialApp( home: S ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...