glide是谷歌推荐的Android图片加载框架,其优秀的缓存策略、Activity的生命周期的继承、GIF图片的支持等都是为人所称道的地方。下面是用glide加载一张图片的调用。
private void loadImage() {
Glide.with(this)
.load("http://pic2.orsoon.com/2017/0118/20170118011032176.jpg")
.into(ivTest);
}
那么,该框架是如何实际运作的呢,我会通过“Glide之旅”系列博客尽可能详细地将我的心得记录下来。“Glide之旅”系列文章汇总:
Glide之旅 —— Registry
Glide之旅 —— DecodeJob
Registry(com.bumptech.glide.Registry)是用来注册管理任务执行对象的管理类,可以简单理解为:Registry是一个工厂,而其中所有注册的对象都是一个工厂员工,当任务分发时,根据当前任务的性质,分发给相应员工进行处理。
Registry是用来负责管理整个glide需要用到的任务执行对象,具体又分为七个注册分支:
com.bumptech.glide.load.model.ModelLoaderRegistry
com.bumptech.glide.provider.EncoderRegistry
com.bumptech.glide.provider.ResourceDecoderRegistry
com.bumptech.glide.provider.ResourceEncoderRegistry
com.bumptech.glide.load.data.DataRewinderRegistry
com.bumptech.glide.load.resource.transcode.TranscoderRegistry
com.bumptech.glide.provider.ImageHeaderParserRegistry
在glide初始化的时候,所有的需求对象就已经被注册了:
package com.bumptech.glide;
...
public class Glide implements ComponentCallbacks2 {
...
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
Glide(Context context,
Engine engine,
MemoryCache memoryCache,
BitmapPool bitmapPool,
ArrayPool arrayPool,
ConnectivityMonitorFactory connectivityMonitorFactory,
int logLevel,
RequestOptions defaultRequestOptions) {
this.engine = engine;
this.bitmapPool = bitmapPool;
this.arrayPool = arrayPool;
this.memoryCache = memoryCache;
this.connectivityMonitorFactory = connectivityMonitorFactory;
DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);
final Resources resources = context.getResources();
registry = new Registry();
registry.register(new DefaultImageHeaderParser());
Downsampler downsampler = new Downsampler(registry.getImageHeaderParsers(),
resources.getDisplayMetrics(), bitmapPool, arrayPool);
ByteBufferGifDecoder byteBufferGifDecoder =
new ByteBufferGifDecoder(context, registry.getImageHeaderParsers(), bitmapPool, arrayPool);
registry.register(ByteBuffer.class, new ByteBufferEncoder())
.register(InputStream.class, new StreamEncoder(arrayPool))
.append(ByteBuffer.class, Bitmap.class, new ByteBufferBitmapDecoder(downsampler))
.append(InputStream.class, Bitmap.class, new StreamBitmapDecoder(downsampler, arrayPool))
.append(ParcelFileDescriptor.class, Bitmap.class, new VideoBitmapDecoder(bitmapPool))
.register(Bitmap.class, new BitmapEncoder())
.append(ByteBuffer.class, BitmapDrawable.class, new BitmapDrawableDecoder<>(resources, bitmapPool, new ByteBufferBitmapDecoder(downsampler)))
.append(InputStream.class, BitmapDrawable.class, new BitmapDrawableDecoder<>(resources, bitmapPool, new StreamBitmapDecoder(downsampler, arrayPool)))
.append(ParcelFileDescriptor.class, BitmapDrawable.class, new BitmapDrawableDecoder<>(resources, bitmapPool, new VideoBitmapDecoder(bitmapPool)))
.register(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, new BitmapEncoder()))
.prepend(InputStream.class, GifDrawable.class, new StreamGifDecoder(registry.getImageHeaderParsers(), byteBufferGifDecoder, arrayPool))
.prepend(ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder)
.register(GifDrawable.class, new GifDrawableEncoder())
.append(GifDecoder.class, GifDecoder.class, new UnitModelLoader.Factory())
.append(GifDecoder.class, Bitmap.class, new GifFrameResourceDecoder(bitmapPool))
.register(new ByteBufferRewinder.Factory())
.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
.append(File.class, InputStream.class, new FileLoader.StreamFactory())
.append(File.class, File.class, new FileDecoder())
.append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
.append(File.class, File.class, new UnitModelLoader.Factory())
.register(new InputStreamRewinder.Factory(arrayPool))
.append(int.class, InputStream.class, new ResourceLoader.StreamFactory(resources))
.append(int.class, ParcelFileDescriptor.class, new ResourceLoader.FileDescriptorFactory(resources))
.append(Integer.class, InputStream.class, new ResourceLoader.StreamFactory(resources))
.append(Integer.class, ParcelFileDescriptor.class, new ResourceLoader.FileDescriptorFactory(resources))
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory())
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
.append(Uri.class, ParcelFileDescriptor.class, new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
.append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new UriLoader.StreamFactory(context.getContentResolver()))
.append(Uri.class, ParcelFileDescriptor.class, new UriLoader.FileDescriptorFactory(context.getContentResolver()))
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
.append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
.append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
.append(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
.append(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
.register(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources, bitmapPool))
.register(Bitmap.class, byte[].class, new BitmapBytesTranscoder())
.register(GifDrawable.class, byte[].class, new GifDrawableBytesTranscoder());
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
glideContext = new GlideContext(context, registry, imageViewTargetFactory, defaultRequestOptions, engine, this, logLevel);
}
...
}
01. new MultiModelLoaderFactory.Entry(GifDecoder.class, GifDecoder.class, new UnitModelLoader.Factory())
02. new MultiModelLoaderFactory.Entry(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
03. new MultiModelLoaderFactory.Entry(File.class, InputStream.class, new FileLoader.StreamFactory())
04. new MultiModelLoaderFactory.Entry(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
05. new MultiModelLoaderFactory.Entry(File.class, File.class, new UnitModelLoader.Factory())
06. new MultiModelLoaderFactory.Entry(int.class, InputStream.class, new ResourceLoader.StreamFactory(resources))
07. new MultiModelLoaderFactory.Entry(int.class, ParcelFileDescriptor.class, new ResourceLoader.FileDescriptorFactory(resources))
08. new MultiModelLoaderFactory.Entry(Integer.class, InputStream.class, new ResourceLoader.StreamFactory(resources))
09. new MultiModelLoaderFactory.Entry(Integer.class, ParcelFileDescriptor.class, new ResourceLoader.FileDescriptorFactory(resources))
10. new MultiModelLoaderFactory.Entry(String.class, InputStream.class, new DataUrlLoader.StreamFactory())
11. new MultiModelLoaderFactory.Entry(String.class, InputStream.class, new StringLoader.StreamFactory())
12. new MultiModelLoaderFactory.Entry(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
13. new MultiModelLoaderFactory.Entry(Uri.class, InputStream.class, new HttpUriLoader.Factory())
14. new MultiModelLoaderFactory.Entry(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
15. new MultiModelLoaderFactory.Entry(Uri.class, ParcelFileDescriptor.class, new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
16. new MultiModelLoaderFactory.Entry(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
17. new MultiModelLoaderFactory.Entry(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
18. new MultiModelLoaderFactory.Entry(Uri.class, InputStream.class, new UriLoader.StreamFactory(context.getContentResolver()))
19. new MultiModelLoaderFactory.Entry(Uri.class, ParcelFileDescriptor.class, new UriLoader.FileDescriptorFactory(context.getContentResolver()))
20. new MultiModelLoaderFactory.Entry(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
21. new MultiModelLoaderFactory.Entry(URL.class, InputStream.class, new UrlLoader.StreamFactory())
22. new MultiModelLoaderFactory.Entry(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
23. new MultiModelLoaderFactory.Entry(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
24. new MultiModelLoaderFactory.Entry(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
25. new MultiModelLoaderFactory.Entry(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
1. new EncoderRegistry.Entry(ByteBuffer.class, new ByteBufferEncoder)
2. new EncoderRegistry.Entry(InputStream.class, new StreamEncoder(arrayPool))
+) new ResourceDecoderRegistry.Entry(ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder)
+) new ResourceDecoderRegistry.Entry(InputStream.class, GifDrawable.class, new StreamGifDecoder(byteBufferGifDecoder, arrayPool))
1. new ResourceDecoderRegistry.Entry(ByteBuffer.class, Bitmap.class, new ByteBufferBitmapDecoder(downsampler))
2. new ResourceDecoderRegistry.Entry(InputStream.class, Bitmap.class, new StreamBitmapDecoder(downsampler,arrayPool))
3. new ResourceDecoderRegistry.Entry(ParcelFileDescriptor.class, Bitmap.class, new VideoBitmapDecoder(bitmapPool))
4. new ResourceDecoderRegistry.Entry(ByteBuffer.class, BitmapDrawable.class, new BitmapDrawableDecoder<>(resources, bitmapPool, new ByteBufferBitmapDecoder(downsampler)))
5. new ResourceDecoderRegistry.Entry(InputStream.class, BitmapDrawable.class, new BitmapDrawableDecoder<>(resources, bitmapPool, new StreamBitmapDecoder(downsampler, arrayPool))
6. new ResourceDecoderRegistry.Entry(ParcelFileDescriptor.class, BitmapDrawable.class, new BitmapDrawableDecoder<>(resources, bitmapPool, new VideoBitmapDecoder(bitmapPool)))
7. new ResourceDecoderRegistry.Entry(GifDecoder.class, Bitmap.class, new GifFrameResourceDecoder(bitmapPool))
8. new ResourceDecoderRegistry.Entry(File.class, File.class, new FileDecoder())
1. new ResourceEncoderRegistry.Entry(Bitmap.class, new BitmapEncoder())
2. new ResourceEncoderRegistry.Entry(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, new BitmapEncoder()))
3. new ResourceEncoderRegistry.Entry(GifDrawable.class, new GifDrawableEncoder())
*) class, new ByteBufferRewinder.Factory()>
*) class, new InputStreamRewinder.Factory(arrayPool)>
1. new TranscoderRegistry.Entry(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources, bitmapPool))
2. new TranscoderRegistry.Entry(Bitmap.class, byte[].class, new BitmapBytesTranscoder())
3. new TranscoderRegistry.Entry(GifDrawable.class, byte[].class, new GifDrawableBytesTranscoder())
1. new DefaultImageHeaderParser()
并不是说在glide内部就已经固定注册对象而不能对已注册的对象进行更替了。通过实现GlideModule(com.bumptech.glide.module.GlideModule)接口可以增加或替换注册对象,比如glide中对okhttp的支持。
package com.bumptech.glide.integration.okhttp3;
...
public class OkHttpGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// Do nothing.
}
@Override
public void registerComponents(Context context, Registry registry) {
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}
}
对象注册了,当然是方便工程运作时方便随时调用,Registry提供了以下接口对注册对象的获取。
getLoadPath(Class, Class
getRegisteredResourceClasses(Class
isResourceEncoderAvailable(Resource>)
getResultEncoder(Resource
getSourceEncoder(X)
getRewinder(X)
getModelLoaders(Model)
getImageHeaderParsers()
1.在ModelLoaderRegistry数据集合中找出Entry第一个传参为String(因为请求的图片地址是一个字符串格式)的数据项,通过上面的数据集合可以知道,符合条件的有:
10. new MultiModelLoaderFactory.Entry(String.class, InputStream.class, new DataUrlLoader.StreamFactory())
11. new MultiModelLoaderFactory.Entry(String.class, InputStream.class, new StringLoader.StreamFactory())
12. new MultiModelLoaderFactory.Entry(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
2.但由于DataUrlLoader#handles(String)大多数情况可能返回false,实际符合条件的也就11和12两项,那么该方法返回的数据集合就由两个MultiModelLoader组成。
3.而在StringLoader.StreamFactory#build(MultiModelLoaderFactory)中调用了MultiModelLoaderFactory.build(Uri.class, InputStream.class),那么,在ModelLoaderRegistry数据集合中找出Entry第一个传参为Uri,第二个传参为InputStream的数据项,符合条件的有:
13. new MultiModelLoaderFactory.Entry(Uri.class, InputStream.class, new HttpUriLoader.Factory())
14. new MultiModelLoaderFactory.Entry(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
16. new MultiModelLoaderFactory.Entry(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
17. new MultiModelLoaderFactory.Entry(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
18. new MultiModelLoaderFactory.Entry(Uri.class, InputStream.class, new UriLoader.StreamFactory(context.getContentResolver()))
20. new MultiModelLoaderFactory.Entry(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
4.由以上可知,会用一个MultiModelLoader存储ModelLoader实现类的集合,集合数据有:
(1) com.bumptech.glide.load.model.stream.HttpUriLoader
(2) com.bumptech.glide.load.model.AssetUriLoader
(3) com.bumptech.glide.load.model.stream.MediaStoreImageThumbLoader
(4) com.bumptech.glide.load.model.stream.MediaStoreVideoThumbLoader
(5) com.bumptech.glide.load.model.UriLoader
(6) com.bumptech.glide.load.model.UrlUriLoader
5.同理,在StringLoader.FileDescriptorFactory#build(MultiModelLoaderFactory)中调用了MultiModelLoaderFactory.build(Uri.class, ParcelFileDescriptor.class),那么,在ModelLoaderRegistry数据集合中找出Entry第一个传参为Uri,第二个传参为ParcelFileDescriptor的数据项,符合条件的有:
15. new MultiModelLoaderFactory.Entry(Uri.class, ParcelFileDescriptor.class, new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
19. new MultiModelLoaderFactory.Entry(Uri.class, ParcelFileDescriptor.class, new UriLoader.FileDescriptorFactory(context.getContentResolver()))
6.由以上可知,会再用一个MultiModelLoader存储ModelLoader实现类的集合,集合数据有:
(1) com.bumptech.glide.load.model.AssetUriLoader
(2) com.bumptech.glide.load.model.UriLoader
7.这样一来,该方法最终返回一个包含两个MultiModelLoader的集合项。
将远程资源解码成数据流(InputStream)后,调用该方法,那么就在EncoderRegistry的数据集合中找出Entry第一个传参为InputStream.class的数据项,通过上面的数据集合可以知道,符合条件的有
2. new EncoderRegistry.Entry(InputStream.class, new StreamEncoder(arrayPool))
而StreamEncoder对象则是该方法的返回项。
该方法需要返回一个LoadPath对象,当获取缓存文件的ByteBuffer时,调用此方法
getLoadPath(ByteBuffer.class, Object.class, Drawable.class)
其中,第二个参数取决于com.bumptech.glide.request.BaseRequestOptions#resourceClass,而默认为Object;第三个参数取决于com.bumptech.glide.RequestManager#as(Class
package com.bumptech.glide;
...
public class RequestManager implements LifecycleListener {
...
public RequestBuilder asDrawable() {
return as(Drawable.class).transition(new DrawableTransitionOptions());
}
}
public RequestBuilder load(@Nullable Object model) {
return asDrawable().load(model);
}
...
}
也就是说,默认第三个参数为Drawable类型,那么获取返回流程如下:
1.在ResourceDecoderRegistry数据集合中找出Entry第一二个传参的基类分别为ByteBuffer和Object的数据项,通过上面的数据集合可以知道,符合条件的有以下三项:
+) new ResourceDecoderRegistry.Entry(ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder)
1. new ResourceDecoderRegistry.Entry(ByteBuffer.class, Bitmap.class, new ByteBufferBitmapDecoder(downsampler))
4. new ResourceDecoderRegistry.Entry(ByteBuffer.class, BitmapDrawable.class, new BitmapDrawableDecoder<>(resources, bitmapPool, new ByteBufferBitmapDecoder(downsampler)))
将这三个Entry的第二个参数作为一个集合(为了区分,简称resource集合)
1. GifDrawable.class
2. Bitmap.class
3. BitmapDrawable.class
2.依次比对集合中的项的基类是否是Drawable(getLoadPath的第三个传参),可知GifDrawable.class和BitmapDrawable.class满足条件,那么分别将getLoadPath的第三个参数即Drawable.class存下来。
3.Bitmap.class不满足条件,那么就在TranscoderRegistry数据集中找出Entry第一二个传参的基类分别为Bitmap和Drawable的数据项,符合条件的显然有一项
1. new TranscoderRegistry.Entry(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources, bitmapPool))
那么,将Entry的第二个参数即BitmapDrawable.class存下来,也就是说,第2、3两步的存储集合(为了区分,简称transcode集合)为
1.Drawable.class
2.BitmapDrawable.class
3.Drawable.class
4.重复第1步操作,只是第二个传参不再是Object.class,而是resource集合中的每个项,而这次需要收集的是ResourceDecoderRegistry.Entry的第三个参数所对应的集合,那么,不难得出集合(为了区分,简称decoder集合)为
byteBufferGifDecoder
new ByteBufferBitmapDecoder(downsampler)
new BitmapDrawableDecoder<>(resources, bitmapPool, new ByteBufferBitmapDecoder(downsampler))
5.在TranscoderRegistry数据集合中找出Entry第一二个传参的基类分别为resource集合的对象和transcode集合的对象的数据项,如果第一个传参是第二个传参的基类,那么就返回UnitTranscoder,否则返回TranscoderRegistry.Entry的第三个参数,那么所对应的集合(为了区分,简称transcoder集合)为
UnitTranscoder
new BitmapDrawableTranscoder(resources, bitmapPool)
UnitTranscoder
6.那么,根据上面的集合,创建一个DecodePath的集合(为了区分,简称decodePaths)
1. new DecodePath(ByteBuffer, GifDrawable, Drawable, [ByteBufferGifDecoder], UnitTranscoder, exceptionListPool)
2. new DecodePath(ByteBuffer, Bitmap, BitmapDrawable, [ByteBufferBitmapDecoder], BitmapDrawableTranscoder, exceptionListPool)
3. new DecodePath(ByteBuffer, BitmapDrawable, Drawable, [BitmapDrawableDecoder], UnitTranscoder, exceptionListPool)
7.现在就可以提取返回值了
new LoadPath<>(ByteBuffer, Object, Drawable, decodePaths, exceptionListPool);
当获取缓存文件的ByteBuffer时,调用此方法
getRewinder(ByteBuffer.class)
在DataRewinderRegistry的数据集中查找以ByteBuffer为键的项,不难发现,有
*) class, new ByteBufferRewinder.Factory()>
然后执行com.bumptech.glide.load.resource.bytes.ByteBufferRewinder.Factory#build(ByteBuffer),其执行结果就是创建一个ByteBufferRewinder的实例,源码如下:
package com.bumptech.glide.load.resource.bytes;
...
public class ByteBufferRewinder implements DataRewinder<ByteBuffer> {
private final ByteBuffer buffer;
public ByteBufferRewinder(ByteBuffer buffer) {
this.buffer = buffer;
}
@Override
public ByteBuffer rewindAndGet() throws IOException {
buffer.position(0);
return buffer;
}
@Override
public void cleanup() {
// Do nothing.
}
public static class Factory implements DataRewinder.Factory<ByteBuffer> {
@Override
public DataRewinder build(ByteBuffer data) {
return new ByteBufferRewinder(data);
}
@Override
public Class getDataClass() {
return ByteBuffer.class;
}
}
}
而这个返回的实例就是该方法的返回值。
当通过com.bumptech.glide.load.resource.bitmap.ByteBufferBitmapDecoder#decode(ByteBuffer, int, int, Options)获取到一个Resource
1. new ResourceEncoderRegistry.Entry(Bitmap.class, new BitmapEncoder())
那么第二个参数new BitmapEncoder()就是该方法需要返回的值。
该方法就是判断getResultEncoder的返回结果是否为空