Glide架構(gòu)分析

疑問

1.如何在圖片下載線程開始時做一個耗時處理
2.如何擴展支持webp動圖
a.分析gif的加載過程
b.分析webp的加載過程

版本

針對Glide以下版本分析

implementation "com.github.bumptech.glide:glide:4.11.0"

用法

Glide加載圖片最簡單的用法如下:

Glide.with(getContext())
  .load("***")
  .into(imageView);

完整流程

從網(wǎng)絡(luò)請求到顯示圖片完整流程大致如下:

注:以上未涉及Transformation等操作
DecoderJob::runWrapped中通過DataFetcherGenerator來指定網(wǎng)絡(luò)流懂扼、文件或本地資源DataFetcher

通過Fetcher獲取到數(shù)據(jù)流后則需要通過decoder實現(xiàn)解碼亚铁。
Decoder的注冊流程如下:

其中RegistersComponents接口使得Glide可以擴展其他decoder等實現(xiàn)侍郭。
在初始化時通過ManifestParser解析出已聲明的GlideModule,然后注冊到Registry药薯。

應(yīng)用程序和庫都可以注冊很多組件來擴展 Glide 的功能吉懊」舨保可用的組件包括:

  1. ModelLoader, 用于加載自定義的 Model(Url, Uri,任意的 POJO )和 Data(InputStreams, FileDescriptors)
  2. ResourceDecoder, 用于對新的 Resources(Drawables, Bitmaps)或新的 Data 類型(InputStreams, FileDescriptors)進(jìn)行解碼
  3. Encoder, 用于向 Glide 的磁盤緩存寫 Data (InputStreams, FileDesciptors)
  4. ResourceTranscoder腕窥,用于在不同的資源類型之間做轉(zhuǎn)換,例如炫狱,從 BitmapResource 轉(zhuǎn)換為 DrawableResource
  5. ResourceEncoder藻懒,用于向 Glide 的磁盤緩存寫 Resources(BitmapResource, DrawableResource)

Decoder注冊

以注冊Decoder為例,append方法參數(shù)中的dataClass视译、resourceClass分別代表什么束析?

/**
 * Appends the given {@link ResourceDecoder} onto the list of available {@link ResourceDecoder}s
 * in this bucket, allowing it to be used if all earlier and default {@link ResourceDecoder}s for
 * the given types in this bucket fail (or there are none).
 *
 * <p>If you're attempting to replace an existing {@link ResourceDecoder} or would like to ensure
 * that your {@link ResourceDecoder} gets the chance to run before an existing {@link
 * ResourceDecoder}, use {@link #prepend(Class, Class, ResourceDecoder)}. This method is best for
 * new types of resources and data or as a way to add an additional fallback decoder for an
 * existing type of data.
 *
 * @see #prepend(String, Class, Class, ResourceDecoder)
 * @see #setResourceDecoderBucketPriorityList(List)
 * @param bucket The bucket identifier to add this decoder to.
 * @param dataClass The data that will be decoded from ({@link java.io.InputStream}, {@link
 *     java.io.FileDescriptor} etc).
 * @param resourceClass The resource that will be decoded to ({@link android.graphics.Bitmap},
 *     {@link com.bumptech.glide.load.resource.gif.GifDrawable} etc).
 * @param decoder The {@link ResourceDecoder} to register.
 */
@NonNull
public <Data, TResource> Registry append(
    @NonNull String bucket,
    @NonNull Class<Data> dataClass,
    @NonNull Class<TResource> resourceClass,
    @NonNull ResourceDecoder<Data, TResource> decoder) {
  decoderRegistry.append(bucket, decoder, dataClass, resourceClass);
  return this;
}
registry
    ...
    /* Bitmaps */
    .append(
        Registry.BUCKET_BITMAP, 
        ByteBuffer.class, 
        Bitmap.class, 
        byteBufferBitmapDecoder)
    .append(
        Registry.BUCKET_BITMAP, 
        InputStream.class, 
        Bitmap.class, 
        streamBitmapDecoder)
    ...
    /* BitmapDrawables */
    .append(
        Registry.BUCKET_BITMAP_DRAWABLE,
        ByteBuffer.class,
        BitmapDrawable.class,
        new BitmapDrawableDecoder<>(resources, byteBufferBitmapDecoder))
    .append(
        Registry.BUCKET_BITMAP_DRAWABLE,
        InputStream.class,
        BitmapDrawable.class,
        new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))
    .append(
        Registry.BUCKET_BITMAP_DRAWABLE,
        ParcelFileDescriptor.class,
        BitmapDrawable.class,
        new BitmapDrawableDecoder<>(resources, parcelFileDescriptorVideoDecoder))
    /* GIFs */
    .append(
        Registry.BUCKET_GIF,
        InputStream.class,
        GifDrawable.class,
        new StreamGifDecoder(imageHeaderParsers, byteBufferGifDecoder, arrayPool))
    .append(
        Registry.BUCKET_GIF, 
        ByteBuffer.class, 
        GifDrawable.class, 
        byteBufferGifDecoder)
  1. bucket是解碼器類型的標(biāo)識KEY
  2. dataClass什么場景下是ByteBuffer、InputStream
    從圖二可以看到data是由DataFetcher獲取的憎亚,不同的DataFetcher會返回不同的dataClass:
/** A DataFetcher that retrieves an {@link java.io.InputStream} for a Url. */
public class HttpUrlFetcher implements DataFetcher<InputStream> {
...
@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));
    }
  }
}
private static final class ByteBufferFetcher implements DataFetcher<ByteBuffer> {
  ...
  @Override
  public void loadData(
      @NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) {
    ByteBuffer result;
    try {
      result = ByteBufferUtil.fromFile(file);
    } catch (IOException e) {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "Failed to obtain ByteBuffer for file", e);
      }
      callback.onLoadFailed(e);
      return;
    }

    callback.onDataReady(result);
  }
  1. resourceClass什么場景使用
    在使用Glide加載圖片時是可以指定resourceClass類型员寇,如下:
// 未指定時默認(rèn)配置
public RequestBuilder<Drawable> asDrawable() {
  return as(Drawable.class);
}

public RequestBuilder<Bitmap> asBitmap() {
  return as(Bitmap.class).apply(DECODE_TYPE_BITMAP);
}

public RequestBuilder<GifDrawable> asGif() {
  return as(GifDrawable.class).apply(DECODE_TYPE_GIF);
}
public <ResourceType> RequestBuilder<ResourceType> as(
    @NonNull Class<ResourceType> resourceClass) {
  return new RequestBuilder<>(glide, this, resourceClass, context);
}
  1. 如何匹配上合適的decoder
Glide.with(getContext())
  .load("***.webp")
  .into(imageView);

如以上代碼dataClass為InputStream、resourceClass為Drawable
在圖二流程getDecodePaths中就會根據(jù)已關(guān)聯(lián)的dataClass第美、resourceClass匹配已注冊的decoder

public synchronized <T, R> List<ResourceDecoder<T, R>> getDecoders(
    @NonNull Class<T> dataClass, @NonNull Class<R> resourceClass) {
  List<ResourceDecoder<T, 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.add((ResourceDecoder<T, R>) entry.decoder);
      }
    }
  }
  // TODO: cache result list.
  return result;
}

在圖二流程loadWithExceptionList中蝶锋,會按注冊順序嘗試decode,成功則返回decode結(jié)果什往,結(jié)束decode流程扳缕。

  1. 在圖四的Decoder的注冊流程中,有一處registerComponents可以注冊定制組件

Registry 類中定義了 prepend() , append() 和 replace() 方法,它們可以用于設(shè)置 Glide每個 ModelLoader 和 ResourceDecoder 之間的順序躯舔。

prepend()

prepend() 將確保你的 ModelLoader 或 ResourceDecoder 先于之前注冊的其他組件并被首先執(zhí)行驴剔。

append()

append() 將確保你的 ModelLoader 或 ResourceDecoder 僅在 Glide 的默認(rèn)組件被嘗試后才會被調(diào)用。

replace()

replace() 將移除所有處理給定模型和數(shù)據(jù)類的 ModelLoaders粥庄,并添加你的 ModelLoader 來代替丧失。

  1. webp動圖插件實現(xiàn)流程簡圖
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市惜互,隨后出現(xiàn)的幾起案子布讹,更是在濱河造成了極大的恐慌,老刑警劉巖训堆,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件描验,死亡現(xiàn)場離奇詭異,居然都是意外死亡坑鱼,警方通過查閱死者的電腦和手機膘流,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鲁沥,“玉大人睡扬,你說我怎么就攤上這事∈蛭觯” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵屎开,是天一觀的道長阐枣。 經(jīng)常有香客問我,道長奄抽,這世上最難降的妖魔是什么蔼两? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮逞度,結(jié)果婚禮上额划,老公的妹妹穿的比我還像新娘。我一直安慰自己档泽,他們只是感情好俊戳,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著馆匿,像睡著了一般抑胎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上渐北,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天阿逃,我揣著相機與錄音,去河邊找鬼。 笑死恃锉,一個胖子當(dāng)著我的面吹牛搀菩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播破托,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼肪跋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了炼团?” 一聲冷哼從身側(cè)響起澎嚣,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吩抓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捆姜,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年晤郑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贸宏。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡造寝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吭练,到底是詐尸還是另有隱情诫龙,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布鲫咽,位于F島的核電站签赃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏分尸。R本人自食惡果不足惜锦聊,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望箩绍。 院中可真熱鬧孔庭,春花似錦、人聲如沸材蛛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卑吭。三九已至构资,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間陨簇,已是汗流浹背吐绵。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工迹淌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人己单。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓唉窃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親纹笼。 傳聞我的和親對象是個殘疾皇子纹份,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

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