Glide源码分析(一) :代码框架和执行流程
Android平台经过多年的发展,涌现出很多图片加载开源框架, 其中使用量非常大的一个开源框架是Glide, Glide是Google推荐的图片框架,并在自家的项目中大量使用的一个非常强大的框架,Glide的功能强大,不仅支持常规的图片加载,还提供Gif,本地Video首帧的解码和显示。
Glide图片框架是用方式上采用链式调用,对于开发者而言使用起来非常简单,开发者可以非常简单的对图片进行配置和二次加工。但是使用简单的背后是极其复杂的代码框架和细节设计,特别是各种资源类型的相互转化,模块的组装。如果没有了解Glide的框架设计思想,源码的阅读过程中很容易陷入代码的迭代流中,面对陡然升级的代码复杂度和信息量,阅读者很容易产生恐惧和迷茫。
1 设计思想
Glide的设计思想是,把每一部分的功能都封装到一个模块中,在Glide的初始化的时候将这些模块进行初始化,在图片请求和加载的过程中根据相关输入参数从Glide实例对象中取得这些模块,将实际执行逻辑交给这些模块执行。这样做的好处是每个模块代码逻辑清晰,不同任务的模块代码边界清晰,方便框架拓展、增加新的业务模块,上层的调用入口不局限于任何对象, 只要底层存在相关处理模块即可, 这也是 Glide框架强大的所在。Glide的本质是提供了入口对象和底层业务模块的调用框架。
1.1模块设计
ResourceDecoder<T, Z>
ResourceDecoder<T, Z>模块作用是将输入的T类型的资源解码转化成Z类型的结果输出, 例如输入资源类型T为InputStream,解码成的结果Z为Bitmap。Glid源码中资源相互转化的模块很多,我们这里就以最常使用的输入InputStream和输出Bitmap观察一下ResourceDecoder<T, Z>的实现StreamBitmapDecoder,其类结构如下:
ResourceDecoder真正实现接口是decode()接口。
DataLoadProvider<T, Z>
DataLoadProvider<T, Z>模块的作用是提供T和Z类型相互转化的Decoder和Encoder,
例如提供上面介绍过的StreamBitmapDecoder,仍然举输入InputStream和输出Bitmap为例,观察一下DataLoadProvider<T, Z>定义和实现:
这里我们主要关心getSourceDecoder()接口,其作用就是返回上面提到的StreamBitmapDecoder
StreamBitmapDecoder<T, Z>
DataFetcher<T>
DataFetcher<T>的作用是,根据某一具体资源封装实现返回数据流或者数据流的封装,例如将Http输入转化为InputStream, 以Http业务为例观察其实现:
其实现接口为loadData(),返回InputStream。
ModelLoader<T, Y>
ModelLoader<T, Y>模块的作用是将T类型的资源输入,返回Y类型的DataFetcher,例如将GlideUrl输入转换成创建创建InputStream的HttpUrlFetcher, 其实现结构如下:
其主要实现接口是getResourceFetcher(),返回HttpUrlFetcher。
ModelLoaderFactory<T, Y>
ModelLoaderFactory<T, Y>从字面意义就能看出来器功能和作用,就是创建相关ModleLoader并将创建好的ModelLoader实例注册到Glide中, 定义和实现如下:
ResourceTranscoder<Z, R>
ResourceTranscoder的做用是将数据流解析的结果转化为另一种类型结果,例如Resource<Bitmap>转化为Resource<GlideDrawable>。以BitmapToGlideDrawableTranscoder为例ResourceTranscoder的实现如下:
Glide在初始化的时候会注册很多业务模块,考虑到这篇文章主要帮助读者理解Glide的设计原理和执行流程,上述模块足够大家理解Glide的设计原理。下面我们进入Glide请求加载资源的流程,看看Glide如何利用上述模块实现图片加载的。
Glide加载图片流程
如果顺着代码的执行流程的每段代码分析下去,由于Glide代码比较复杂,读完一段代码逻辑后,很容易将上一段内容忘记,不容易宏观上了解整套代码的执行流程,采用时序图的方式可以比较直观的了解每一段业务代码所在的时序,并能帮助读者复盘前面分析的代码内容,Glide的整体执行时序如下:
下面我们Glide加载图片时序图来分析源码,具体分析每一段的代码的逻辑。
Glide构造函数
Glide构造函数中初始化了上面介绍到的各种模块,代码如下:
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
。。。。。。
//DataLoadProvider内存缓存,用于其他模块从Glide中获取DataLoadProvider
dataLoadProviderRegistry = new DataLoadProviderRegistry();
//InputStream解码为Bitmap的DataLoadProvider
StreamBitmapDataLoadProvider streamBitmapLoadProvider =
new StreamBitmapDataLoadProvider(bitmapPool, decodeFormat);
dataLoadProviderRegistry.register(InputStream.class, Bitmap.class, streamBitmapLoadProvider);
//File解码为Bitmap的DataLoadProvider
FileDescriptorBitmapDataLoadProvider fileDescriptorLoadProvider =
new FileDescriptorBitmapDataLoadProvider(bitmapPool, decodeFormat);
dataLoadProviderRegistry.register(ParcelFileDescriptor.class, Bitmap.class, fileDescriptorLoadProvider);
//封装了StreamBitmapDataLoadProvider和FileDescriptorBitmapDataLoadProvider,支持InputStream和File解码成Bitmap
ImageVideoDataLoadProvider imageVideoDataLoadProvider =
new ImageVideoDataLoadProvider(streamBitmapLoadProvider, fileDescriptorLoadProvider);
dataLoadProviderRegistry.register(ImageVideoWrapper.class, Bitmap.class, imageVideoDataLoadProvider);
//InputStream解码为GifDrawable的DataLoadProvider
GifDrawableLoadProvider gifDrawableLoadProvider =
new GifDrawableLoadProvider(context, bitmapPool);
dataLoadProviderRegistry.register(InputStream.class, GifDrawable.class, gifDrawableLoadProvider);
//封装了ImageVideoDataLoadProvider和GifDrawableLoadProvider, 支持InputStream和File转化为GifBitmapWrapper, GifBitmapWrapper封装了GlideDrawable和GifDrawable
dataLoadProviderRegistry.register(ImageVideoWrapper.class, GifBitmapWrapper.class,
new ImageVideoGifDrawableLoadProvider(imageVideoDataLoadProvider, gifDrawableLoadProvider, bitmapPool));
//File转化为ParcelFileDescriptor的ModelLoader工厂类, FileDescriptorFileLoader.中获取
register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
//从File中获取InputStream
register(File.class, InputStream.class, new StreamFileLoader.Factory());
//从资源Id中获取文件资源描述符
register(int.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
register(int.class, InputStream.class, new StreamResourceLoader.Factory());
register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
//从资源Id中获取InputStream, 例如res/drawable中的图片资源
register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());
//从String中获取ParcelFileDescriptor
register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());
register(String.class, InputStream.class, new StreamStringLoader.Factory());
//从Uri中获取ParcelFileDescriptor
register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());
//从Uri中获取InputStream
register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
//从URL中获取InputStream
register(URL.class, InputStream.class, new StreamUrlLoader.Factory());
//从GlideUrl中获取InputStream
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
//将byte字节转化为InpuStream流
register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());
//将Btima资源类型转化为GlideBitmapDrawable类型
transcoderRegistry.register(Bitmap.class, GlideBitmapDrawable.class,
new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool));
//GifBitmapWrapper转化为GlideDrawable资源类型
transcoderRegistry.register(GifBitmapWrapper.class, GlideDrawable.class,
new GifBitmapWrapperDrawableTranscoder(
new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool)));
。。。。。。
}
Glide构造函数中会初始化DataLoadProvider、ModelLoader工厂类和ResourceTransonder,这些模块将会在接下来的流程被调用。
Glide.with()
Glide.with() 的作用创建RequestManager, RequestManager的作用是管理图片请求的生命周期和创建GenericRequestBuilder, 这里不做过多的描述。
RequestManager.load()
RequestManager.load()是根据输入的资源来源创建相关的从Glide中查找已经注册好的资源模块,然后构造GenericRequestBuilder, 我们就以load()接口输入我String类型的Url地址类为例, 分析一下其核心函数和执行流程:
loadGeneric()方法,代码如下:
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
//首先从Glide中查找注册好的ModelLoader
//modelClass为String.class, ModelLoader为StreamStringLoader
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
//modelClass为String.class, ModelLoader为FileDescriptorResourceLoader
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);
//创建DrawableTypeRequest实例对象,封装了StreamStringLoader, FileDescriptorResourceLoader, Glide等,DrawableTypeRequest
//DrawableTypeRequest是GenericRequest子类。等于创建一个GenericRequest实例对象。
return optionsApplier.apply(
new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}
下面我们来看一下DrawableTypeRequest构造函数到底做了哪些工作,代码如下:
DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
super(context, modelClass,
buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
GlideDrawable.class, null),
glide, requestTracker, lifecycle);
this.streamModelLoader = streamModelLoader;
this.fileDescriptorModelLoader = fileDescriptorModelLoader;
this.optionsApplier = optionsApplier;
}
DrawableTypeRequest的构造函数,初始化了父类的构造函数和自身的一些变量,
其中buildProvider()是比较关键的一个方法,其代码如下:
private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
ModelLoader<A, InputStream> streamModelLoader,
ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
Class<R> transcodedClass,
ResourceTranscoder<Z, R> transcoder) {
/*从Glide中获取ResourceTranscoder
*resourceClass是GifBitmapWrapper.class, transcodedClass是GlideDrawable.class。
*transcoder是GifBitmapWrapperDrawableTranscoder实例对象
*/
if (transcoder == null) {
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
//从Glide中获取DataLoaderProvider, 实际获取为ImageVideoDataLoadProvider
DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
//创建新的ModelLoader
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
fileDescriptorModelLoader);
//将modelLoader, transcoder和dataLoadProvider封装到FixedLoadProvider
return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}
//buildProvider()方法的执行逻辑已经分析完了,下面我们来看一下DrawableTypeRequest父类到底会执行哪些操作,
GenericRequestBuilder(Context context, Class<ModelType> modelClass,
LoadProvider<ModelType, DataType, ResourceType, TranscodeType> loadProvider,
Class<TranscodeType> transcodeClass, Glide glide, RequestTracker requestTracker, Lifecycle lifecycle) {
this.context = context;
//modelClass是String.class
this.modelClass = modelClass;
//transcodeClass是GlideDrawable.clss
this.transcodeClass = transcodeClass;
this.glide = glide;
//将buildProvider()创建的FixedLoadProvider封装到ChildLoadProvider ????多此一举~_~
this.loadProvider = loadProvider != null
? new ChildLoadProvider<ModelType, DataType, ResourceType, TranscodeType>(loadProvider) : null;
}
DrawableTypeRequest的父类是GenericRequestBuilder, 其主要功能封装一些初始化参数,并将buildProvider()创建的DataLoadProvider封装到ChildLoadProvider 中。GenericRequestBuilder主要作用是通过初始化好参数去构建GenericRequest。
GenericRequestBuilder.into()
首先我们来Review一下GenericRequestBuilder.into()方法的源码, 其源码如下:
public <Y extends Target<TranscodeType>> Y into(Y target) {
。。。。。。
//创建Request, 实际是GenericRequest。
Request request = buildRequest(target);
//将Reqesut添加到lifecycle, Glide之所以能够暂停和恢复加载图片,就是在于
//图片加载请求被添加到生命周期回调中
lifecycle.addListener(target);
//执行图片加载请求
requestTracker.runRequest(request);
return target;
}
GenericRequestBuilder.into()代码逻辑相对简单,就是创建Reqeust交个requestTracker执行Reqeust。requestTracker.runRequest()方法的逻辑相对简单,限于篇幅和内容的要求,这里不做分析。下面我们来看一下Target是如何创建的,举我们最常使用的GenericRequestBuilder.into(ImageView)为例。Glide源码内有一个Target工厂类,里面封装了各种T创建Target的方法。 代码如下:
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target<Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new DrawableImageViewTarget(view);
}
。。。。。。
}
关于GenericRequestBuilder的内容我们先分析到这里。下面我们分析一下Request的创建源码和和执行。
GenericRequest.onSizeReady()
上面我们分析到requestTracker会执行Request,实际是执行GenericRequest的begin()方法。GenericRequest.begin()方法会调用到onSizeReady()方法,其源码如下:
public void onSizeReady(int width, int height) {
。。。。。。
//modelLoader 是ImageVideoModelLoader
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
//dataFetcher是ImageVideoFetcher
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
//transcoder是GifBitmapWrapperDrawableTranscoder
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
//执行Engine.load()方法
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
}
GenericRequest.onSizeReady()作用是获取前面创建的DataFetcher、ModelLoader和Transcoder交给Engine去执行。
Engine.load()
Engine.load()方法的源码如下:
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
。。。。。。
//创建DecodeJob
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
//创建EngineRunnable实例对象
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
//执行EngineRunnable任务
engineJob.start(runnable);
。。。。。。
}
Engine.load()中会创建DecodeJob,然后封装到EngineRunnable任务中去去执行。限于篇幅和内容Engine.load()方法里的其他内容暂不分析。EngineRunnable的run()发方法源码如下:
public void run() {
。。。。。。
//获取解析资源的结果GlideBitmapDrawableResource, decode()方法会调用
//到DecodeJob的decodeFromSource()方法。
resource = decode();
//回调给需要加载的资源组件
onLoadComplete(resource);
。。。。。。
}
run() 方法的逻辑比较简单,就是请求解析资源,然后解析结果回调给需要加载资源的组件。下面我们来看一下DecodeJob是如何解析资源的。
DecodeJob
上面我们了解到EngineRunnable的decode()方法会调用到DecodeJob的decodeFromSource(), 其源码如下:
public Resource<Z> decodeFromSource() throws Exception {
//解析请求资源资源
Resource<T> decoded = decodeSource();
//将解析好的资源进行尺寸剪裁等,重新封装
return transformEncodeAndTranscode(decoded);
}
decodeFromSource()方法的逻辑就是解析请求的资源,把解析的结果进行二次处理(图片剪裁等)。下面我们继续分析decodeSource()方法,decodeSource()作用是获取文件流然后将文件流解析成图片资源。其源码如下:
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
//A类型为ImageVideoWrapper, ImageVideoWrapper里封装了InputStream和FileDescriptor两种数据流自愿
final A data = fetcher.loadData(priority);
//将数据流解析
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
//第一节的内容我们了解到DataFetcher的作用是获取文件流,这里fetcher是ImageVideoFetcher,其核心方法是loadData(), loadData()返回的数据流是ImageVideoWrapper, ImageVideoWrapper里封装了InputStream和FileDescriptor两种数据流。具体封装代码参考ImageVideoFetcher类。获取完文件流后会调用decodeFromSourceData()方法继续解析资源。decodeFromSourceData()源码如下:
private Resource<T> decodeFromSourceData(A data) throws IOException {
。。。。。。
//调用Decoder解析数据流,返回解析结果。A是ImageVideoWrapper, Decoder是ImageVideoBitmapDecoder
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
。。。。。。
return decoded;
}
decodeFromSourceData()作用是调用Decoder解析数据流,输入的数据流是ImageVideoWrapper, Decoder是ImageVideoBitmapDecoder。ImageVideoBitmapDecoder会根据输入流是url还是文件描述符来决定是否调用StreamBitmapDecoder还是FileDescriptorBitmapDecoder。我们就以StreamBitmapDecoder来分析一下是如何解析数据流的。其源码如下:
public Resource<Bitmap> decode(InputStream source, int width, int height) {
//通过downsampler,下载数据解析成Bitmap
Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
//将Bitmap封装到Resource<Z>中
return BitmapResource.obtain(bitmap, bitmapPool);
}
StreamBitmapDecoder.decode()作用就是通过Downsampler下载文件流,解析成Bitmap,让后将Bitmap封装到Resource<Z>中。Downsampler.decode()会调用到Downsampler.decodeStream(),其源码如下:
private static Bitmap decodeStream(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options) {
。。。。。。
//通过BitmapFactory解析文件流获取Bitmap
final Bitmap result = BitmapFactory.decodeStream(is, null, options);
return result;
}
decodeStream()作用就是将InputStream交给BitmapFactory获取Bitmp。 输入解析成Bitmap分析完毕。
回到前面decodeFromSource()方法,在数据解析完后,会继续调用transformEncodeAndTranscode()将ResourceBitmap重新处理封装。transformEncodeAndTranscode()会调用transcoder的transcode()方法。其源码如下:
public Resource<GlideBitmapDrawable> transcode(Resource<Bitmap> toTranscode) {
//创建GlideBitmapDrawable实例对象,从toTranscode获取Bitmap封装到GlideBitmapDrawable,
//本质是个Drawable对象。
GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get());
//将GlideBitmapDrawable封装到GlideBitmapDrawableResource中。
return new GlideBitmapDrawableResource(drawable, bitmapPool);
}
transcoder作用是将Bitmap转化为Drawale类型资源。
继续回到EngineRunnable对象的run()方法中,解析好的GlideBitmapDrawableResource会被onLoadComplete()方法回调。最终回调到GenericRequest的onResourceReady()方法中,其源码如下:
public void onResourceReady(Resource<?> resource) {
//resource是GlideBitmapDrawableResource, Object是GlideBitmapDrawable
Object received = resource.get();
//回调onResourceReady()方法
onResourceReady(resource, (R) received);
}
GenericRequest的onResourceReady()逻辑就是从GlideBitmapDrawableResource中获取GlideBitmapDrawable,然后回调onResourceReady(Resource<?> resource, R result)方法, 源码如下:
private void onResourceReady(Resource<?> resource, R result) {
//将资源result设置到target中。
target.onResourceReady(result, animation);
}
其作用是将将资源result设置到target中。以ImageView的Target目标为例,我们来分析一下onResourceReady()源码,源码如下:
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
。。。。。。
//调用父类的onResourceReady()方法
super.onResourceReady(resource, animation);
this.resource = resource;
}
其父类的的onResourceReady()回调用到的 setResource()方法,其源码如下:
protected void setResource(GlideDrawable resource) {
//将Drawable对象设置到view中
view.setImageDrawable(resource);
}
setResource()将GlideDrawable对象设置到View。支持整个源码流程分析完毕。
总结
相对于其他第三方图片加载开源框架,Glide有点在于其拥有生命周期管理。代码框架设计抽象、内部耦合性小拓展方便,业务组件和通过注册的方式去支持实现。但是Glide的框架实现过于复杂,泛型使用广泛,构造函数过多,看着着实眼花缭乱,不利于短时间内消化。正应对那句话简洁不简单。 对于开源项目的初学者来说,Glide并不是一个好的项目,门槛太高,本篇分析希望成为源码分析者的一个参考,希望大家提出宝贵意见,继续优化Glide分析文档。