疑问
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 的功能。可用的组件包括:
- ModelLoader, 用于加载自定义的 Model(Url, Uri,任意的 POJO )和 Data(InputStreams, FileDescriptors)
- ResourceDecoder, 用于对新的 Resources(Drawables, Bitmaps)或新的 Data 类型(InputStreams, FileDescriptors)进行解码
- Encoder, 用于向 Glide 的磁盘缓存写 Data (InputStreams, FileDesciptors)
- ResourceTranscoder,用于在不同的资源类型之间做转换,例如,从 BitmapResource 转换为 DrawableResource
- 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)
- bucket是解码器类型的标识KEY
- 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 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 {
...
@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);
}
- 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);
}
- 如何匹配上合适的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流程。
- 在图四的Decoder的注册流程中,有一处registerComponents可以注册定制组件
在 Registry 类中定义了 prepend() , append() 和 replace() 方法,它们可以用于设置 Glide每个 ModelLoader 和 ResourceDecoder 之间的顺序。
prepend()
prepend() 将确保你的 ModelLoader 或 ResourceDecoder 先于之前注册的其他组件并被首先执行。
append()
append() 将确保你的 ModelLoader 或 ResourceDecoder 仅在 Glide 的默认组件被尝试后才会被调用。
replace()
replace() 将移除所有处理给定模型和数据类的 ModelLoaders,并添加你的 ModelLoader 来代替。
- webp动图插件实现流程简图