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 的功能??捎玫慕M件包括:

  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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容