目前在android项目上,图片加载库有很多选择,Glide是主流的加载库之一,作为一个被google推荐的开源库,它有着跟随页面周期、支持gif和webp、支持多种数据源等特点,并且使用起来很简单,本篇文章将会分析Glide的加载流程,但是Glide的源码比较复杂,我将从Glide的最简单使用分析Glide是如何去加载一张图片的。
(一)从Glide.with(context)入手
Glide.with(context).load(url).asBitmap().into(imageView);
通常,这将是最简单的使用Glide的方式,那么首先我们进入Glide这个类看看:
Glide.wth(context)会返回一个RequestManager对象,我们发现Glide重载了多个with方法,返回值都是去调用RequestManagerRetriever.get()方法:
RequestManagerRetriever类中会对context加以区分后走对应的get方法,首先判断如果不是主线程,则用ApplicationContext调用get方法:
直接new了一个RequestManager对象,将Appcation级别的信息传递了进入,那么如果是在主线程调用,无论context是fragment还是activtiy,最后都会走fragmentGet和supportFragmentGet这两个方法:
这两个方法都是向当前页面添加一个fragment,一个是添加v4包的,一个是添加android.app.Fragment,我们从fragmentGet看起,首先通过getRequestManagerFragment获得了一个RequestManagerFragment对象:
很简单,先从缓存里找,没有就new一个再添加上去。
回到fragmentGet,接着从改fragment中获取RequestManager,因为是才new的,所以RequestManager为空,那么接着new了RequestManagert同时将fragment的lifecycle对象传递进去,set给fragment后返回了manager。
因为Glide.wth(context)最后就是返回了一个RequestManagert对象,所以这一步到这里结束了,那添加的这个fragment有什么用呢,我们分别打开RequestManagerFragment和RequestManager看看:
可以看到RequestManagerFragment在初始化的时候创建了一个ActivityFragmentLifecycle对象,在下面三个生命周期的时候分别调用了ActivityFragmentLifecycle对应的方法。
在RequestManager的构造方法中,最后传进来的lifecycle分别add(this)和addListener(connectivityMonitor),所以当RequestManagerFragment对应周期调用时,RequestManager也会调用对应方法,其实光看类名我们就能明白RequestManager是对每个ImageRequest进行管理的类,那么这样一来,每个ImageRequest就和Fragment的生命周期同步,所以Glide是这样做到请求与页面周期同步的。我们再来看看具体是如何管理的以及加入的connectivityMonitor的作用:
RequestManager的三个方法中,最后都是调用requestTracker的方法,所以requestTracker是最后的request容器。再看看最开始的lifecycle.addListener(connectivityMonitor),它由ConnectivityMonitor connectivityMonitor = factory.build(context, new RequestManagerConnectivityListener(requestTracker)),构造而来,也将requestTracker传递了进去:
加载图片自然是要联网了,进入DefaultConnectivityMonitor看看:
既然它被lifecycle添加进去了,那么一定有对的几个方法,这个类其实主要是注册了一个广播去监听网络状况,当网络状况变化的时候调用listener.onConnectivityChanged(isConnected),这个listener就是一开始传递进来的RequestManagerConnectivityListener了:
所以最终的作用其实是在网络重连的时候重启request,那么Glide.with(context)的主要流程我们就走完了。
一张图总结如下
(二)load()和asBitmap()
在with()方法之后将返回RequestManager对象,那么接着我们直接去RequestManager的load(String)方法看看:
追着流程下去最后是进入了loadGeneric(Class modelClass)方法,该方法首先生成了两个ModelLoader对象,然后将这两个对象合着其他的内容传入了一个新的DrawableTypeRequest对象中,虽然最后的返回还调用了optionsApplier.apply(),但是我们知道Glide.with(context).load(url)返回的就是一个DrawableTypeRequest对象,所以我们大概可以判断最后就是返回这个新生成的DrawableTypeRequest对象了。
这里的两个ModelLoader生成过程太过复杂就不分析,我们直接跟着主流程走下去
但是我们可以通过断点知道这里获得的两个对象是StreamStringLoader和FileDescriptorStringLoader,接着我们看看optionsApplier是什么:
在RequestManager的构造方法中optionsApplier由new了一个新的OptionsApplier对象赋值,那么我们看看OptionsApplier的apply方法:
调用options.apply之后就把传入的对象返回了,这个options我们从来没有设置过,同时在debug中它也确实为空,那么确实和我们猜测的一样,loadGeneric()中直接返回了new的DrawableTypeRequest对象,紧接着调用了它的load方法:
可见load方法只是对model赋值,这里传入的model就是url,最后返回本身,那么Glide.with(context).load(url)也走完了,接着我们来看DrawableRequestBuilder的asBitmap()方法
生成了一个BitmapTypeRequest对象并将DrawableRequestBuilder的相关配置传入且返回,所以整个Glide.with(context).load(url).asBitmap()还是比较清楚的,最后返回了一个BitmapTypeRequest对象
(三)复杂的into()
讲完了前几个步骤现在只剩下最后的的into()方法了,目前的几个步骤只是传递了参数,但是实际的图片请求毛都没见着,所以你应该可以预计into()方法做的事很多了,into()方法是在BitmapTypeRequest父类中实现的:
我们并没有做scaleType的配置,接着看glide.buildImageViewTarget(view, transcodeClass)
在Glide类中很清楚发现接着该找ImageViewTargetFactory.buildTarget()
ImageViewTargetFactory代码很简单,是作为一个工具类使用的,根据传递的class生成对应的ImageViewTarget,根据我们调用的Glide.with(context).load(url).asBitmap()其实我们可以猜测这里的clazz是Bitmap.class,我们还是去理一理怎么来的:
看了构造方法我们可以发现BitmapTypeRequest确实传递了Bitmap.class进去,那么into()方法的传递参数是一个BitmapImageViewTarget对象
在接下来into()方法中首先从target中取出之前的request,将之前的request回收掉,接着调用buildRequest(target)创建了一个新的request添加到requestTracker中,并且将target添加到lifecycle中,这列lifecycle对象就是一开始的ActivityFragmentLifecycle,那么这个request也将会跟着一开始添加的fragment生命周期运行了。在RequestTracker中,运行request其实是调用request.begin(),那么我们进入buildRequest(target)找找看生成的request这个方法是做了什么:
thumbnail相关是设置略缩图的,这里我们不用管,直接去看obtainRequest(target, sizeMultiplier, priority, parentCoordinator)
追踪下去可以看到最后生成的是GenericRequest对象,再接着看看begin()方法:
我们先看下面的代码:
如果是在正常状态,那么就调用target.onLoadStarted,那么我们回到BitmapImageViewTarget,onLoadStarted是在父类ImageViewTarget里重写的,我们看看干了什么:
所以是将我们传递给into()方法的ImageView设置了一张图片,原来我们在使用glide设置placeHolder图片就是在这个时候设置进来的了。再回到begin()方法,最上面是设置erorHolder的方法,只要model等于空,同样的设置错误站位图,这个model是什么呢,还记得我们在分析Glide.with().load()方法的源码吗:
传入的url最后传递给model,而这个model最后在生成GenericRequest的时候又传递给了它:
所以加载之前的判断条件是url是不是为空,为空则为异常。
回到begin()方法,上面是异常,下面是默认展位图,那么中间就是加载图片的部分了:
Util.isValidDimensions判断是否设置了有效的overrideWidth和overrideHeight,我们没有设置过直接走else,target是BitmapImageViewTarget,在它的父类ViewTarget中实现了getSize()方法:
依次追下去,我们发现最后调用的SizeDetermine.getSize(),currentWidth和currentHeight是展示加载的图片的ImageView的有效宽高,通常都可以获取到,那么我们直接去看cb.onSizeReady(currentWidth, currentHeight),这里传进来的cb也就是GenericRequest本身了。
ImageView的宽高进来之后,计算了一个设置的宽高,因为默认sizeMultiplier是为1的,如果没有设置过sizeMultiplier默认设置宽高即为ImageView的宽高,之后的modelLoader和dataFetcher又得绕回去了,我们回去看看DrawableTypeRequest的构造方法:
在DrawableTypeRequest的构造方法中调用了super构造方法,其中第三个参数传递了一个LoadProvider,这个provider就是之后传递到GenericRequest的loadProvider对象,buildProvider()方法一起贴出来了,返回了一个FixedLoadProvider对象,我们进入FixedLoadProvider发现getModelLoader返回的就是构造时传入的ImageVideoModelLoader对象,并且getTranscoder返回的也是构造时传递的transcoder,
我们跟着glide.buildTranscoder(resourceClass, transcodedClass)的流程,看看transcoder是什么:
我们知道传递进来的decodedClass和transcodedClass分别是GifBitmapWrapper.class和GlideDrawable.class,那么这里最后会由factories.get(GET_KEY)返回结果,而在Glide类的构造方法中有一句这样的初始化代码:
在这里注册了GifBitmapWrapperDrawableTranscoder,那么最后返回就是GifBitmapWrapperDrawableTranscoder了,
回到onSizeReady()方法,这些参数和其他一些配置都一起传递给了engine.load(),那我们找下engine是如何生成的:
GenericRequestBuilder.obtainRequest()在创建GenericRequest的时候获取了glide的engine对象传递了进去,而glide初始化是通过GlideBuilder完成的:
在创建engine的时候,将cache和线程相关传递进去了,那么我们可以确定,图片的加载最后是交给engine处理了:
在这个方法中首先根据配置生成了对应的key,之后是获取缓存的操作,我们先不看,因为还不知道是如何设置缓存的,我们直接看最下面的几段代码,首先根据key创建了一个EngineJob对象,其次根据配置选项创建了一个DecodeJob对象,随后将他们传入了一个EngineRunnable对象中,那么加载图片就是在这个runnable中执行了,调用了engineJob.start(runnable)之后就开始加载图片:
加载图片是通过decode()方法进行了,这里判断了是否要走缓存,我们直接不看缓存直接走decodeFromSource(),虽然EngineRunnable默认是走一次缓存的,但是第一次加载是没有缓存的,最后还是走decodeFromSource()
在DecodeJob中decodeSource()首先通过fetcher.loadData()获取data,这个fetcher是啥呢,我们重新回顾下DecodeJob的生成:
fetcher是这么传进来的,loadProvider.getModelLoader()返回的是ImageVideoModelLoader:
所以这里最后返回了一个ImageVideoModelLoader对象
随后进入了loadData()方法,其实在这里fileDescriptor获取为空,这是因为最开始初始化的原因,也是我一开始说的StreamStringLoader和FileDescriptorStringLoader生成太过复杂,我们只走主流程,streamFethcer是个OkHttpStreamFetcher对象,那么我们看看它的loadData()方法:
从OkHttpStreamFetcher名字可以看出,我所使用的这个版本的Glide是使用okhttp去加载图片,确实loadData里也是使用okhttp去加载的
在OkHttpUrlLoader$Factory中确实也初始化了OkHttpClient
回到ImageVideoModelLoader,fetcher.loadData()返回的内容会封装在ImageVideoWrapper对象中回到DecodeJob返回,接着会把他传递到decodeFromSourceData()处理:
我们没有做过磁盘缓存的配置,直接进入else判断,首先我们看loadProvider.getSourceDecoder()是什么:
这里的dataLoadProvider是初始化的时候传入的:
由上面的步骤可知,根据FixedLoadProvider初始化出入的class,生成的是ImageVideoGifDrawableLoadProvider对象,而ImageVideoGifDrawableLoadProvider.getSourceDecoder()则返回GifBitmapWrapperResourceDecoder:
那么接着看GifBitmapWrapperResourceDecoder.decode():
GifBitmapWrapperResourceDecode的流程比较清楚,由于我们设置的是bitmap,按照流程下去最后会在decodeBitmapWrapper()返回了一个GifBitmapWrapper对象,这里还需要看看bitmapDecoder.decode(toDecode, width, height),虽然后面经过的类比较多,但是其实都只提供了一个方法去实现功能,那么这里我直接断点跟着流程进去了:
bitmapDecoder是一个ImageVideoBitmapDecoder对象,其中又调用了streamDecoder.decode(is, width, height):
StreamBitmapDecoder再调用了downsampler.decode(source, bitmapPool, width, height, decodeFormat):
Downsampler类中最后生成的bitmap就是调用BitmapFactory.decodeStream()获得的,这个bitmap会被封装进Resource对象返回,最后封装成回到EngineRunnable.run()返回,并传入onLoadComplete():
这个结果会回传到EngineJob通过handler回调至handleResultOnMainThread()方法:
在这个方法中又将结果通过ResourceCallback回传,而在Engine.load()方法中我们将之前的GenericRequest添加进cbs了,所以这里又会回到GenericRequest:
在onResourceReady()方法中调用了target.onResourceReady(result, animation),还记得我们在调用into(ImageView)方法时ImageView被封装进了BitmapImageViewTarget吗,这里的target就是它了,:
终于,到这里图片被加载下来并设置上view了!
回头看看Engine.load()中之前遗留下来的的东西:
这三个处理分别从cache、activeResources、jobs去取内容,我们在获取到bitmap传递内容的时候封装的Resource对象会又被封装进EngineResource在Engine之间传递,在EngineResource中有个释放资源的方法,里面的listener就是Engine:
也就是在释放资源的时候内存中做了缓存,而activeResources是保存当前活跃的Resource,分别会在Complete()和获取cache的时候保存Resource,Released()的时候移除Resource:
jobs也类似,在任务完成或者取消的时候会移除job,如果发起的任务已有就不重复发起: