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).
 *
 * 

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 Registry append( @NonNull String bucket, @NonNull Class dataClass, @NonNull Class resourceClass, @NonNull ResourceDecoder 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 {
...
@Override
public void loadData(
    @NonNull Priority priority, @NonNull DataCallback 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 {
  ...
  @Override
  public void loadData(
      @NonNull Priority priority, @NonNull DataCallback 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 asDrawable() {
  return as(Drawable.class);
}

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

public RequestBuilder asGif() {
  return as(GifDrawable.class).apply(DECODE_TYPE_GIF);
}
public  RequestBuilder as(
    @NonNull Class 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  List> getDecoders(
    @NonNull Class dataClass, @NonNull Class resourceClass) {
  List> result = new ArrayList<>();
  for (String bucket : bucketPriorityList) {
    List> entries = decoders.get(bucket);
    if (entries == null) {
      continue;
    }
    for (Entry entry : entries) {
      if (entry.handles(dataClass, resourceClass)) {
        result.add((ResourceDecoder) 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动图插件实现流程简图

你可能感兴趣的:(Glide架构分析)