Glide架构分析

疑问

1.如何在图片下载线程开始时做一个耗时处理
2.如何扩展支持webp动图
a.分析gif的加载过程
b.分析webp的加载过程

版本

针对Glide以下版本分析

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

用法

Glide加载图片最简单的用法如下:

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

完整流程

从网络请求到显示图片完整流程大致如下:

注:以上未涉及Transformation等操作
DecoderJob::runWrapped中通过DataFetcherGenerator来指定网络流、文件或本地资源DataFetcher

通过Fetcher获取到数据流后则需要通过decoder实现解码。
Decoder的注册流程如下:

其中RegistersComponents接口使得Glide可以扩展其他decoder等实现。
在初始化时通过ManifestParser解析出已声明的GlideModule,然后注册到Registry。

应用程序和库都可以注册很多组件来扩展 Glide 的功能。可用的组件包括:

  1. ModelLoader, 用于加载自定义的 Model(Url, Uri,任意的 POJO )和 Data(InputStreams, FileDescriptors)
  2. ResourceDecoder, 用于对新的 Resources(Drawables, Bitmaps)或新的 Data 类型(InputStreams, FileDescriptors)进行解码
  3. Encoder, 用于向 Glide 的磁盘缓存写 Data (InputStreams, FileDesciptors)
  4. ResourceTranscoder,用于在不同的资源类型之间做转换,例如,从 BitmapResource 转换为 DrawableResource
  5. ResourceEncoder,用于向 Glide 的磁盘缓存写 Resources(BitmapResource, DrawableResource)

Decoder注册

以注册Decoder为例,append方法参数中的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是解码器类型的标识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类型,如下:
// 未指定时默认配置
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中就会根据已关联的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结果,结束decode流程。

  1. 在图四的Decoder的注册流程中,有一处registerComponents可以注册定制组件

Registry 类中定义了 prepend() , append() 和 replace() 方法,它们可以用于设置 Glide每个 ModelLoader 和 ResourceDecoder 之间的顺序。

prepend()

prepend() 将确保你的 ModelLoader 或 ResourceDecoder 先于之前注册的其他组件并被首先执行。

append()

append() 将确保你的 ModelLoader 或 ResourceDecoder 仅在 Glide 的默认组件被尝试后才会被调用。

replace()

replace() 将移除所有处理给定模型和数据类的 ModelLoaders,并添加你的 ModelLoader 来代替。

  1. webp动图插件实现流程简图
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,941评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,397评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,345评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,851评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,868评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,688评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,414评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,319评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,775评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,945评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,096评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,789评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,437评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,993评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,107评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,308评论 3 372
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,037评论 2 355

推荐阅读更多精彩内容