Glide
简单使用
Glide.with(Context)
.load(url)
.into(view);
框架都是从简单的思想发展出来的,只是用了一些特殊的方式来实现和扩展。
加载图片最简单的思想:
根据地址获取图片--> 按照显示区域放大或者缩小 --> 设置给ImageView。
Glide也是这样的,按照这个思路进行学习。
Glide.with(context)
根据context 和所在的线程,绑定生命周期。
如果是在主线程,且Context 可以定位到一个Activity,那么就创建一个没有view的frament放在Activity里,请求回绑定fragment的生命周期,也就是Activity的生命周期。
如果是子线程,或者Application的Context,那么就和Application绑定。
这一步会创建一个RequestManager。
asBitmap().apply(xxx).load(url).listener(xxx)
通过asBitmap 创建一个RequestBuilder,之后的操作相当与给RequestBuilder设置参数,顾名思义,最后会创建一个Request。
into(view)
关键的最后一步
1.根据imageView的scaleType和目标格式(asBitmap和asDrawable 还是asGif)来创建一个Target。
根据target和设置的RequestOption创建一个Request。如果和view之前的request相同,则回收现在的request,再执行一遍之前的request。
否则,使用requestManager的track方法执行request。不在运行时,则调用request.begin方法开始执行,一般创建的是SingleRequest。
SingleReqeust的begin方法,先判断是否在运行,如果正在运行,则抛出异常。如果已经完成,则直接回掉onResourceReady。否则,设置当前状态为WAITING_FOR_SIZE,然后根据宽高,调用onSizeReady方法。
5.设置状态为RUNNING,根据宽高和各种请求参数等调用engine.load方法去加载。
6.Engine的load方法 回根据宽高等参数生成一个key,然后开始查找资源。
1)如果没有设置skipMemoryCache,则从memory中查找,如果找到了,则从memoryCache中移除,并且以弱引用的方式加入activeResource中。然后回掉onReasourceReady,结束。memoryCache会在每次释放一个View的内容时,如果可缓存将其加入。
2)没找到,则从activeResource中查找,找到了则返回并会掉OnResourceReady,否则删除这个弱引用。
3)依然没有找到,从job列表中查找是否有相同的请求,如果有,则在这个请求上再添加上自己的callback,否则则创建一个engineJob,一个decodeJob。并将engineJob加入job列表,然后通过start方法传入docodeJob进行执行。
其实主要任务时decodeJob进行的,engineJob只是一个回掉作用。。。
4) decodeJob会被engine执行,如果缓存策略时全部缓存。那么接下来的顺序如下。
glide将源数据分为两种:
Data:获取到的原始数据,原始图片。
Resource:Data转换后的图片。
比如原图1000*1000,目标区域是500*500,那么data是1000的图片,resource是500的。
1.第一步,如果当前缓存策略可以使用resource缓存,创建ResourceCahceGenerator查找resource缓存。
ResourceCacheGenerator 根据DiskLruCache查找磁盘缓存的File,找到的话,通过Registry查找可以处理File的ModelLoader列表。ModelLoader 是表示可以处理一类输入数据的一个处理器,Model就是指源数据类型(这里源数据是file)。 Glide默认有FileLoader。
遍历这些ModelLoader,创建LoadData对象,并使用该对象加载model(这里是File),会创建FileFetcher来load,并以当前的ResourceCacheGenerator作为回掉callback。 源码中打开文件有两种方式,一种是打开为InputStream,一种是ParcelFileDescriptor。
打开成功onDataReady失败则onLoadFailed。
ResourceGenerator又回回掉onDataFetcherReady,这里又重新回到DecodeJob。
DecodeJob保存这些数据(假如是InputStream),设置理由为DECODE_DATA然后回掉EngineJob 重新调度当前任务(reschedule)。
重新调度回再次执行DecodeJob的run方法,本次理由改变,最终会执行decodeFromRetrievedData。这里面根据当前的数据类型和资源类型,从registry中找到可以将该数据类型(InputStream)转换至目标资源类型(Bitmap、Drawable、GifDrawable)的加载器,并最终找到一个ResourceDecoder调用他的decode方法处理成目标资源对象(假如为Bitmap)。
重新回到DecodeJob,成功加载再到资源后,回掉callback的onResourceReady(engineJob),engineJob会通过handler切换到主线程并回调所有的callback(ResourceCallback,来自Engine,Engine的callback又来自与SingleRequest),SingleRequest中又会最终回调到我们设置的RequestListener,如果我们的RequestListener的onResourceReady没有返回Ready,就会调用Target的onResourceReady,这里回真正设置Bitmap或者Drawable给ImageView。
除了Glide默认的ResourceDecoder之外,还可以自定义ResourceDecoder。通过继承AppGlideModule,可以在registerComponenets中通过registry注册自己的方法。如下
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
//意思是,注册一个可以将InputStream 转化为GifDrawble的BpgInputStreamDecoder到列表最前面的位置。
// 注册一个将InputStream转为Bitmap的BpgBitmapDecoder 到列表最后的位置
registry.prepend(InputStream.class, GifDrawable.class, new BpgInputStreamDecoder(context))
.append(InputStream.class, Bitmap.class, new BpgBitmapDecoder());
}
而自定义ResourceDecoder如下图
继承了ResourceDecoder,泛型为InputStream 和 Bitmap,表示这个ResourceDecoder是将InputStream转化为Bitmap。
重写handles方法:判断当前的ResourceDecoder是否可以处理这个source。
重写docode方法,根据width、height和options,将source转化为bitmap。
其他Generator的流程类似,从服务器获取的话多了请求和返回的流程。
整体流程如下图:
从红色的start处开始,之前的流程很好debug,就不加入了。