使用默认的内存缓存和磁盘缓存(可自定义),并可以选择缓存使用策略,全局可以操作缓存
glide提供了对bitmap对象的缓存、重用、回收特性,使内存更友好
可以加载网络资源、本地资源等,以及支持GIF图
可以为图片请求同时设置缩略图请求
可自定义图片的变换操作、解析格式、裁剪方式,resize大小等,包括缩放比例及自定义动画
可以设置加载成功失败的占位图及相应监听
可以暂停、恢复、取消加载,并与activity、fragment等生命周期相关联
Glide.with(activity)//拿到界面对应的RequestManager对象
.load(url)//load一个url
.override(width, height)//重新裁剪大小
.skipMemoryCache(true)//内存缓存策略(不读取也不存入内存)
.diskCacheStrategy(DiskCacheStrategy.ALL)//磁盘缓存策略(存放图片的原文件和此次请求最终处理后的图片到本地缓存)
.into(imageView);//显示到ImageView中
glide默认的内存缓存实现是LruResourceCache类,维护一个Lru规则的LinkedList存储key和结果
glide的key是自定义的一个Key类对象,维护了一些信息作为key的实体,如url,width等
由于glide的结果支持多种类型而不是简单的Bitmap,所以Resource>类来封装,而map的value是对应的Resource对象
默认的缓存大小最大为堆内存的40%(正常由设备的长宽高及一些常量决定的);这里说的内存缓存包括对Resource的缓存以及bitmap的缓存(BitmapPool)
我们可以自己实现MemoryCache类来设置到Glide的全局环境中
在android3.0开始,bitmap对象像素数据存放在java堆内存中,再解码图片时,可以复用已有的未recycle的bitmap对象来节省内存,所以glide默认提供了LruBitmapPool类来进行bitmap的回收(3.0开始);
在android3.0~4.4中,bitmap的复用只支持像素数据大小相同的图片使用;4.4开始,支持了对于不同大小像素数据复用bitmap的功能;所以glide提供了两种策略(LruPoolStrategy类)进行对bitmap的缓存适配
我们可以自己实现BitmapPool来设置到Glide的全局环境中
glide在内存缓存的策略上,使用了二级内存缓存+bitmap的缓存,大大提高了内存使用率;
二级内存缓存其中之一是一个LruResourceCache对象,就是常规认为的LinkedHashMap来维护的内存缓存,将Key-Resource对象进行保存,并在超过最大大小时按Lru策略释放;
二级内存缓存另一个是一个HashMap对象,叫做activeResources,其维护的是Key-WeakReference对象,顾名思义,它是用来存放当前正在被使用的资源的,且有多少存放多少,不会像一级缓存一样通过Lru策略限制大小,并且其value维护的是一个弱引用,不会影响其对象正常释放,这就体现出二级缓存的一个优势—提高了一定的命中率(如果认为当前使用的就有可能还会使用的话)
下面来看二级缓存另一个优势,也是实现二级缓存的主要原因:上面说过glide会使用一个LruBitmapPool来进行对bitmap的复用,那么这就有一个问题,就是如何确定哪些bitmap对象可以复用,尤其是针对正在使用的bitmap对象(只有一级缓存的话就有可能在cache中了),如果正在使用却被复用了岂不是出错了么?所以glide使用二级缓存将内存中的和正在使用的资源分离开来,这样,如上右图右半部分所示,当从内存缓存中移除时,就可以直接放入bitmapPool中了,因为内存中的肯定是没有在被使用的;而当bitmapPool中移除的时候就可以直接调用bitmap的recycle方法使其释放资源;
而资源什么时候应该放入cache或active中呢?如上图所示,在加载资源时会依次在cache和active中查找,如果在cache中找到了则要放入active中;如果是新加载的资源也会放入active中;在释放资源时,会先从active中移除资源,然后尝试放入cache中;这些流程就确保了active中的都是正在使用的资源,cache中的都是没有被使用的资源;
那么还剩最后一个问题,就是如何确认一个资源是否正在被使用呢?glide使用了引用计数法来标识,如上图,从active中获取的资源都会使其引用计数加一,而每次在资源的释放方法调用时该计数会减一,直到引用计数减为0时证明没有再被引用了,然后就可以将其放入cache了
glide默认提供了DiskLruCache类作为磁盘缓存
磁盘缓存默认最大空间为250MB,路径为应用cache文件夹下的image_manager_disk_cache文件夹
磁盘缓存的key由一个SafeKeyGenerator对象根据原始的Key对象进行生成,有关glide的key的说明下面会单独说
glide磁盘缓存的一大特点是可以双重缓存:就是资源的原始大小的缓存以及每次裁剪后的最终资源的缓存,这样一来当请求相同规则的资源时可以直接从磁盘缓存获取,而不需要加载出原始文件进行裁剪,也就是以空间换时间;这些缓存策略可以设置(DiskCacheStrategy);
我们也可以定义自己的DiskCache对象进行设置
Glide以一个EngineKey对象当作key,里面会存有真实的key(比如url等),还有一些其他请求参数,比如大小、转换等,重写其equals方法,当这些属性都一样时认为相等,所以这个EngineKey对象就可以作为result资源的key,从内存缓存或者result磁盘缓存读取时都以这个为key
而OriginalKey对象其实就存了EngineKey的真实key,并重写其equals方法,所以这个对象就可以作为source资源的key,从source磁盘缓存读取时就以这个为key
而写入磁盘缓存时,会将无论是EngineKey还是OriginalKey的字段通过SHA-256加密并转成16进制字符串当作名字存入本地
由图可知,Glide对象通过一个GlideBuilder类构建,也是全局单单例
虽然api提供了相关方法可以使我们自己创建并设置Glide对象(和picasso一样),但是glide已经不推荐这种方法,而是提供了一个GlideModule类,其两个接口方法提供了构建单例对象时的Builder和Glide对象供我们操作,我们只需要定义自己的GlideModule,并在manifest文件里进行的配置即可应用
比如我们想引进OkHttp作为网络加载库,在gradle文件里导入想过依赖后,就会发现多了一个OkHttpGlideModule类,其registerComponents方法里注册了OkHttp相关的组件,在使用网络请求时就会使用到这些组件了;有关glide提供的组件的概念下面会单独说,也是glide整个解析、加载、编码解码的框架设计核心
我们可以看到,Glide.with()方法的参数可以是activity、fragment等,这就意味着请求会绑定到界面上
每次调用with方法,Glide会根据参数类型从RequestManagerRetriever单例类中获取该参数对应的RequestManager对象,如下代码(以FragmentActivity为例)
public RequestManager get(FragmentActivity activity) {
...
FragmentManager fm = activity.getSupportFragmentManager();//拿到FragmentManager
return supportFragmentGet(activity, fm);
}
RequestManager supportFragmentGet(Context context, FragmentManager fm) {
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);//拿到FragmentManager对应的fragment
RequestManager requestManager = current.getRequestManager();//拿到fragment对应的RequestManager
if (requestManager == null) {//没有则新建
//传入了fragment的lifecycleListener
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) {
SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);//拿到FragmentManager对应的fragment
if (current == null) {//没有则建一个自定义的无界面fragment并加入FragmentManager用作关联生命周期
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = new SupportRequestManagerFragment();
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
...
lifecycle.addListener(this);//将自己加入到fragment的lifecycleListener中,这样fragment的生命周期就会调用到此类中,完成请求的状态管理
}
由代码可知,每个activity会拥有一个RequestManager以及一个无界面的fragment在其中;
在fragment构建的时候会生成一个Lifecycle对象,其内部可以添加其他lifecycle对象;
在fragment的生命周期里会调用相应的lifecycle对象,内部就会调用加入的其他lifecycle对象的相应方法,而requestManager自身就是一个Lifecycle对象,加入到fragment时会添加到其lifecycle中,这样一来,相当于fragment的生命周期会调用到RequestManager的相应方法,RequestManager就可以管理请求的状态了
在我们调用load(xxx)方法后,返回的是一个新的GenericRequestBuilder的子类对象,这个对象是包装各种请求参数用的,其相关方法为链式调用,其之所以是有继承结构是因为glide支持多种类型的资源,不同类型有着不同的XxxRequestBuilder构建类
GenericRequestBuilder对象提供了各种占位图的设置方法、缓存策略的方法,裁剪大小、比例的方法,还提供了缩略图请求设置以及自定义动画的设置方法
其各个子类也有一些设置方法如裁剪方式等
在我们最终发起请求调用into()方法时,这个builder对象就会构建真正的GenericRequest对象(也有可能是缩略图请求对象),该对象保有着这些请求参数
由图可知,Target类就是处理最终资源的类,其本身也是个LifecycleListener,可以绑定生命周期做操作;该接口提供了各种处理结果的方法;该接口的TranscodeTo泛型是为了支持多种类型的资源,后续会说到;
在我们调用into()方法时,glide会根据transcodeTo的类型来创建不同的target
我们最常用的就是ImageView作为承载对象,对应生成的就是glide提供的ViewTarget的某一种,简单来说作用就是为imageView设置图片的处理
这里要说明的是,Glide的一大优势是默认自动获取imageView的大小进行最基本的资源裁剪,当然我们也可以自定义裁剪大小:在发起请求时,会先看是不是自己设置了裁剪大小,是则真正发起请求,不是则去获取ViewTarget的大小;ViewTarget大小如果已经可以取到,则真正发起请求,如果不可以,则会设置一个OnPreDrawListener到view上,当获取到了大小后通过回调通知,发起真正请求;
RequestTracker对象其实就是管理一个界面request状态的一个类,有类图也可看出,在RequestManager对象创建出来时也会创建一个RequestTracker对象
一个界面的所有Request对象的begin、pause、cancel等都是由这个类进行的
这几个类作为一个模块来讲,是因为他们几个联合起来完成了一次请求各个阶段的运行:由图知,在Request开始请求时,调用engine(在glide初始化时创建)的load()方法开始
(1)engine内部会先尝试从一级二级缓存中取出资源,如果有缓存则直接通过回调返回
(2)否则会创建一个EngineJob对象、一个EngineRunnable对象和一个DecodeJob对象,由EngineJob对象作为manager管理EngineRunnable的提交和回调等,代码如下
//EngineJob开启一个EngineRunnable
public void start(EngineRunnable engineRunnable) {
this.engineRunnable = engineRunnable;
future = diskCacheService.submit(engineRunnable);//使用diskCache的ExecutorService线程池进行处理
}
//EngineRunnable的run方法
public void run() {
...
resource = decode();//加载数据
...
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
//EngineRunnable的decode方法
private Resource<?> decode() throws Exception {
if (isDecodingFromCache()) {//当前状态是从DiskCache加载
return decodeFromCache();
} else {//当前状态是从数据源加载
return decodeFromSource();
}
}
//EngineRunnable获取资源失败
private void onLoadFailed(Exception e) {
if (isDecodingFromCache()) {//当前为从DiskCache获取状态
stage = Stage.SOURCE;//改为从数据源获取
manager.submitForSource(this);//重新提交
} else {
manager.onException(e);
}
}
//重新提交EngineRunnable
public void submitForSource(EngineRunnable runnable) {
future = sourceService.submit(runnable);//使用source的ExecutorService线程池进行处理
}
由代码可知,EngineJob负责根据不同状态提交EngineRunnable对象,并接收回调,以此来管理着加载流程,这里可以看到glide是将从disk获取的任务和从数据源获取的任务分为两个线程池来进行,使得二者更好的协同工作,互不影响
(3)EngineRunnable对象内部负责从disk或者数据源取得数据的流程
//从diskCache获取
private Resource<?> decodeFromCache() throws Exception {
Resource<?> result = null;
result = decodeJob.decodeResultFromCache();
if (result == null) {
result = decodeJob.decodeSourceFromCache();
}
return result;
}
//从数据源获取
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
由代码看出,从diskCache获取时,先去尝试获取result缓存(上面说到过的缓存结果的策略),如果没有则尝试获取source缓存(源文件),如果还没有则重新提交,去数据源获取数据,所有的这些步骤其实都是由DecodeJob完成的,下面我们来看看DecodeJob
(4)我们从流程图看下DecodeJob是如何从不同源获取数据并进行缓存的
我们可以看到,DecodeJob管理着从disk里取出result或者source资源的流程,以及从数据源获取数据的流程,其中从diskCache中获取的流程主要为:
通过key在DiskCache中找到result(Result缓存),有的话加载出来资源,转换成目标对象(transcode)进行返回;
如果没有Result缓存则从DiskCache里找到source(Source缓存),有的话加载出来资源,进行transformation转化为最终资源,再放入Result缓存,最后进行转换(transcode)并返回
从数据源获取的主要流程为:
以上的加载、转化、转换、放入缓存等操作使用到的DataFetcher、Transformation、Decoder、Encoder、Transcoder对象都来自于DataLoadProvider对象,这也是上面提到的glide实现多种类型资源的核心设计,下面就来好好看看这块是如何设计的!
对Glide而言,将数据处理分为了几个阶段,每个阶段都对应的不同的类型:
首先是我们的请求的"地址",这个地址可以是url、uri、file、resource资源等等,glide将其抽象为一个泛型,我在这里称其为Model,在我们调用load(xxx)方法时传入的参数,glide会根据其相应的类型做处理
有了Model,那么根据Model请求最终得到的数据源理论上也可以是很多类型,虽然大部分时候都是InputStream,glide也将其抽象为一个泛型,我在这里称其为DecodeFrom,因为处理数据时会将这个数据源编码成相应的数据对象,所以叫其DecodeFrom,意为将这个数据源编码到xxx
那么将DecodeFrom编码成的类型也会有多种,比如最简单的Bitmap,或者glide自己要处理的gif、GlideDrawable对象等等,所以将其也抽象为一个泛型,我在这里称其为DecodeTo
当得到DecodeTo类型的结果后,glide会对其做一次类型的转换,转换为glide最终处理需要的类型,这个过程叫做transcode转码,也被抽象为一个泛型,我在这里称其为TranscodeTo
有了上述处理流程和类型,glide其实是通过给每个阶段提供了相应类型(用泛型表示)的模型类实现不同阶段的处理,下面来看看这些模型类:
首先第一步是从Model到DecodeFrom的流程,举个最简单的例子的话我们可以理解为从url到InputStream的过程,这一步glide提供的模型类是ModelLoader
而DataFetcher声明的方法loadData()的返回值看出,其返回的就应该是一个DecodeFrom类型的对象;
以上面类图为例,我们引入了OkHttp的库作为网络请求库,那么其就会向glide里注册一个OkHttpUrlLoader,可以看到其泛型是GlideUrl(Model)→InputStream(DecodeFrom),也就是会把我们的请求的地址作为一个GlideUrl转换为一个InputStream,其提供的OkHttpStreamFetcher也就会将这个glideUrl转换为InputStream,简单看下其代码就能大概明白什么意思了:
//ModelLoader
public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
...
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
return new OkHttpStreamFetcher(client, model);//构建DataFetcher,传入Model(GlideUrl)
}
}
//DataFetcher
public class OkHttpStreamFetcher implements DataFetcher<InputStream> {
...
@Override
public InputStream loadData(Priority priority) throws Exception {
Request.Builder requestBuilder = new Request.Builder()
.url(url.toStringUrl());//通过url构建相应请求
...
Request request = requestBuilder.build();
Response response = client.newCall(request).execute();
responseBody = response.body();
...
stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
return stream;//返回InputStream
}
}
而这些ModelLoader是如何注册到glide当中的我们在后面会另说
上述处理后,我们拿到了DecodeFrom了,假设是InputStream,那么需要将其解析成真正的资源对象,也就是DecodeTo类型,比如最简单的Bitmap,那么glide为这一过程提供的模型类就是ResourceDecoder,由其泛型声明和提供的decode方法可看出,作用是将DecodeFrom转换为DecodeTo,这里我们就拿最简单的InputStream转换为Bitmap的ResourceDecoder类来说:
//decode方法
@Override
public Resource<Bitmap> decode(InputStream source, int width, int height) {
Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
return BitmapResource.obtain(bitmap, bitmapPool);
}
这里需要提一下的是glide的结果类型都是Resource类对象,其实就可以理解为将xxx对象包装了一下,不同的Resource对象有一些特殊处理罢了,所以上面的类图的各种返回值都是Resouce对象,只是泛型不太一样,代表的是不同类型的资源;
StreamBitmap的decode方法,就是将InputStream(DecodeFrom)进行一系列的解析(decode、复用bitmap等)生成bitmap对象,最后又包了一下成为返回类型Resource
还有其他的一些ResourceDecoder用于其他类型的转换,下面也会提到
有了解码decode,相应的就要有encode—将不同的资源写入缓存中,于是glide设计了Encoder类,其泛型相对于自身功能来说可以声明为WrittenFrom,即从xxx写到xxx,其提供的方法encode的第二个参数为OutputStream对象,也就是说该类就是将WrittenFrom类型的资源写入到os中;
之所以这里叫WrittenFrom不叫DecodeFrom或者DecodeTo,是因为写入缓存有两种途径,一种是从数据源直接写入缓存(InputStream→OutputStream),一种是从现有资源写入缓存(Resource→OutputStream),后者其实是用于将转换后的最终资源(Result)写入缓存,所以对于后者来说,其WrittenFrom类型一定是Resource,于是glide提供了一个ResourceEncoder类,把泛型声明重写了一下,用于把资源写入缓存;
而从数据源直接写入缓存的就是Encoder了;
这里我们可以看个最简单的例子:将InputStream写入缓存的StreamEncoder
public class StreamEncoder implements Encoder<InputStream> {
private static final String TAG = "StreamEncoder";
@Override
public boolean encode(InputStream data, OutputStream os) {
byte[] buffer = ByteArrayPool.get().getBytes();
try {
int read;
while ((read = data.read(buffer)) != -1) {//就是最基本的读写流操作
os.write(buffer, 0, read);
}
return true;
...
}
}
当我们生成Bitmap(DecodeTo)后,因为glide支持gif、bitmap等不同数据的操作,需要有不同的处理包装bitmap的类,所以glide提供了ResourceTranscoder类,来将DecodeTo类型转换为TranscodeTo类型,我们来看一个transcoder的例子:
//ResourceTranscoder
public class GlideBitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, GlideBitmapDrawable> {
private final Resources resources;
private final BitmapPool bitmapPool;
...
@Override
public Resource<GlideBitmapDrawable> transcode(Resource<Bitmap> toTranscode) {
GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get());
return new GlideBitmapDrawableResource(drawable, bitmapPool);
}
}
//GlideBitmapDrawable
public class GlideBitmapDrawable extends GlideDrawable {
...
GlideBitmapDrawable(Resources res, BitmapState state) {
if (state == null) {
throw new NullPointerException("BitmapState must not be null");
}
this.state = state;
final int targetDensity;
if (res != null) {
final int density = res.getDisplayMetrics().densityDpi;
targetDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density;
state.targetDensity = targetDensity;
} else {
targetDensity = state.targetDensity;
}
width = state.bitmap.getScaledWidth(targetDensity);
height = state.bitmap.getScaledHeight(targetDensity);
}
...
@Override
public void draw(Canvas canvas) {
if (applyGravity) {
Gravity.apply(BitmapState.GRAVITY, width, height, getBounds(), destRect);
applyGravity = false;
}
canvas.drawBitmap(state.bitmap, null, destRect, state.paint);
}
...
}
//GlideBitmapDrawableResource
public class GlideBitmapDrawableResource extends DrawableResource<GlideBitmapDrawable> {
private final BitmapPool bitmapPool;
public GlideBitmapDrawableResource(GlideBitmapDrawable drawable, BitmapPool bitmapPool) {
super(drawable);
this.bitmapPool = bitmapPool;
}
@Override
public int getSize() {
return Util.getBitmapByteSize(drawable.getBitmap());
}
@Override
public void recycle() {
bitmapPool.put(drawable.getBitmap());
}
}
由代码可以看到,GlideBitmapDrawableTranscoder是将一个Resource对象转换为一个Resource对象;GlideBitmapDrawable对象其实就是包装了一个bitmap的GlideDrawable对象(Glide自定义绘制的Drawable对象);而生成的GlideBitmapDrawableResource对象就是包装了GlideBitmapDrawable对象的一个Resource对象,提供回收、获取size等Resource功能
这里再说下Transformation这个类,用过图片库的都知道,加载图片时我们可以设置其显示方式,比如centerCrop、fitCenter等,glide也可以,并将其抽象为Transformation类,其泛型也很好理解了,就是将一个Resource对象transformation一下,生成最终的一个Resource对象
现在,我们再来看下DataLoadProvider这个组件,他的泛型声明为DecodeFrom和DecodeTo,也就是说他是用来提供将数据源转换为数据的功能的;他的四个方法:
getCacheDecoder():返回的是一个从File转换为DecodeTo类型的ResourceDecoder对象,再结合其名字就不难理解其功能—提供从磁盘缓存转换到Bitmap(DecodeTo)的Decoder
getSourceDecoder():返回的是一个从DecodeFrom转换到DecodeTo的ResourceDecoder对象,与上面的相对应,就是提供了一个从InputStream转换到Bitmap的Decoder
getSourceEncoder():返回的是一个将DecodeFrom写入OutputStream的Encoder,即将数据源(InputStream)直接写入disk缓存的功能
getEncoder():返回的是一个将Resource资源直接写入缓存的ResourceEncoder,即将已有资源写入缓存的功能
所以,这个组件提供了缓存、数据源之间的读写功能
继承自DataLoadProvider的LoadProvider组件就是多支持了两个泛型Model和TranscodeTo,以及相应的组件ModelLoader和Transcoder
在一个GenericRequestBuilder对象构建时,glide就会根据类型等条件构建出LoadProvider,有了这个LoadProvider,就可以得到一次请求流程用到的所有相关组件了
//创建RequestBuilder时
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);
...
}
//根据各种类型构建LoadProvider
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) {
if (streamModelLoader == null && fileDescriptorModelLoader == null) {
return null;
}
if (transcoder == null) {
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
fileDescriptorModelLoader);
return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}
上面我们说的这么多,那么这些所谓的组件都在什么时候有的呢,我们使用时也没发现有这些东西的存在呀,下面我们就来看看glide是在何时注册了这些组件以及如何使用的:
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
...
loaderFactory = new GenericLoaderFactory(context);
...
dataLoadProviderRegistry = new DataLoadProviderRegistry();
//注册各种glide默认提供的DataLoader
StreamBitmapDataLoadProvider streamBitmapLoadProvider =
new StreamBitmapDataLoadProvider(bitmapPool, decodeFormat);
dataLoadProviderRegistry.register(InputStream.class, Bitmap.class, streamBitmapLoadProvider);
...
ImageVideoDataLoadProvider imageVideoDataLoadProvider =
new ImageVideoDataLoadProvider(streamBitmapLoadProvider, fileDescriptorLoadProvider);
dataLoadProviderRegistry.register(ImageVideoWrapper.class, Bitmap.class, imageVideoDataLoadProvider);
...
//注册各种glide默认提供的ModelLoader
register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
register(File.class, InputStream.class, new StreamFileLoader.Factory());
...
//注册默认提供的transcoder
transcoderRegistry.register(Bitmap.class, GlideBitmapDrawable.class,
new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool));
transcoderRegistry.register(GifBitmapWrapper.class, GlideDrawable.class,
new GifBitmapWrapperDrawableTranscoder(
new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool)));
//注册默认提供的transformation
bitmapCenterCrop = new CenterCrop(bitmapPool);
drawableCenterCrop = new GifBitmapWrapperTransformation(bitmapPool, bitmapCenterCrop);
bitmapFitCenter = new FitCenter(bitmapPool);
drawableFitCenter = new GifBitmapWrapperTransformation(bitmapPool, bitmapFitCenter);
}
由代码可以看出,在Glide单例对象创建时,会注册各种默认提供的组件,注册时用class对象来表示泛型,那么在构建request时,通过不同class对象进行get,就可以得到相应的组件了;
在每个DataLoader里面会生成其对应需要的Decoder、Encoder;
还可以看出,这些组件很多都是包装关系,及层层嵌套调用,每层完成自己的特殊处理,所以我们在实际中可以发现,一个简单的String为Model的请求生成的ModelLoader不是简单的String->InputStream这么简单,而是经过了一层层的包装,举个例子就是:你的String的Model生成的ModelLoader里会再生成一个以Uri为Model的ModelLoader,其里面又会生成一个以GlideUrl为Model的ModelLoader,这个ModelLoader才是我们最终处理用到的ModelLoader—也是OkHttp注册的GlideUrl→InputStream的ModelLoader;之所以要分这么多层也是为了把每个步骤抽象出来,比如String的ModelLoader里需要将String转换为Uri,再交由Uri的ModelLoader处理,Uri的ModelLoader需要判断uri是不是本地以及其schema等最终决定哪个DataFetcher,对其框架的扩展性极好;
其他几个组件也都是这样一层层包装下去的;
知道了这些组件的作用,我们就更加明确了glide加载流程了:
以上把各个模块作用及实现做了简单分析,下面来看看glide整体的运行流程,将这些模块串联起来
我们就以主流程Glide.with(activity).load(url).into(imageView);来分析加载过程
Glide.with(activity)拿到界面绑定的唯一RequestManager,使请求与声明周期绑定
requestManager.load(url),根据请求参数的类型找到相应的ModelLoader等组件,构建LoadProvider,并创建出DrawableRequestBuilder对象
into(ImageView),会根据设置或者ImageView的scaleType设置transformation,并根据transcode类型构建相应的Target对象,开始请求
请求时先将view上原有的request和资源清空(或回收或释放),然后根据设置的裁剪尺寸或view自身的尺寸开始请求流程
先依次从两级内存缓存中查找(根据策略),命中则回调通知,否则先到磁盘缓存里找
磁盘缓存依次寻找result和source资源(根据策略),命中后通过MainHandler回调到主线程,进行callback回调,没有命中则从数据源请求
通过modelLoader从数据源进行请求,并完成磁盘缓存,最终生成Resouce资源,通过MainHandler回调到主线程,进行callback回调
ImageView的Target作为回调拿到Resource后,将Drawable设置到ImageView上完成加载