Android Glide浅析

作者: im_the_best | 来源:发表于2018-08-09 21:13 被阅读25次

    目前在android项目上,图片加载库有很多选择,Glide是主流的加载库之一,作为一个被google推荐的开源库,它有着跟随页面周期、支持gif和webp、支持多种数据源等特点,并且使用起来很简单,本篇文章将会分析Glide的加载流程,但是Glide的源码比较复杂,我将从Glide的最简单使用分析Glide是如何去加载一张图片的。

    (一)从Glide.with(context)入手

                                                                                          Glide.with(context).load(url).asBitmap().into(imageView);

    通常,这将是最简单的使用Glide的方式,那么首先我们进入Glide这个类看看:

    glide的with方法

    Glide.wth(context)会返回一个RequestManager对象,我们发现Glide重载了多个with方法,返回值都是去调用RequestManagerRetriever.get()方法:

    glide的get方法

    RequestManagerRetriever类中会对context加以区分后走对应的get方法,首先判断如果不是主线程,则用ApplicationContext调用get方法:

    通用的get方法 getApplicationManager

    直接new了一个RequestManager对象,将Appcation级别的信息传递了进入,那么如果是在主线程调用,无论context是fragment还是activtiy,最后都会走fragmentGet和supportFragmentGet这两个方法:

    获取fragment

    这两个方法都是向当前页面添加一个fragment,一个是添加v4包的,一个是添加android.app.Fragment,我们从fragmentGet看起,首先通过getRequestManagerFragment获得了一个RequestManagerFragment对象:

    配置requestManager

    很简单,先从缓存里找,没有就new一个再添加上去。

    回到fragmentGet,接着从改fragment中获取RequestManager,因为是才new的,所以RequestManager为空,那么接着new了RequestManagert同时将fragment的lifecycle对象传递进去,set给fragment后返回了manager。

    因为Glide.wth(context)最后就是返回了一个RequestManagert对象,所以这一步到这里结束了,那添加的这个fragment有什么用呢,我们分别打开RequestManagerFragment和RequestManager看看:

    RequestManagerFragment部分代码 RequestManagerFragment部分代码

    可以看到RequestManagerFragment在初始化的时候创建了一个ActivityFragmentLifecycle对象,在下面三个生命周期的时候分别调用了ActivityFragmentLifecycle对应的方法。

    RequestManager部分代码

    在RequestManager的构造方法中,最后传进来的lifecycle分别add(this)和addListener(connectivityMonitor),所以当RequestManagerFragment对应周期调用时,RequestManager也会调用对应方法,其实光看类名我们就能明白RequestManager是对每个ImageRequest进行管理的类,那么这样一来,每个ImageRequest就和Fragment的生命周期同步,所以Glide是这样做到请求与页面周期同步的。我们再来看看具体是如何管理的以及加入的connectivityMonitor的作用:

    RequestManager部分代码

    RequestManager的三个方法中,最后都是调用requestTracker的方法,所以requestTracker是最后的request容器。再看看最开始的lifecycle.addListener(connectivityMonitor),它由ConnectivityMonitor connectivityMonitor = factory.build(context,   new RequestManagerConnectivityListener(requestTracker)),构造而来,也将requestTracker传递了进去:

    ConnectivityMonitorFactory

    加载图片自然是要联网了,进入DefaultConnectivityMonitor看看:

    DefaultConnectivityMonitor简化代码 DefaultConnectivityMonitor简化代码 DefaultConnectivityMonitor简化代码

    既然它被lifecycle添加进去了,那么一定有对的几个方法,这个类其实主要是注册了一个广播去监听网络状况,当网络状况变化的时候调用listener.onConnectivityChanged(isConnected),这个listener就是一开始传递进来的RequestManagerConnectivityListener了:

    RequestManagerConnectivityListener

    所以最终的作用其实是在网络重连的时候重启request,那么Glide.with(context)的主要流程我们就走完了。

    一张图总结如下

    fragment与request周期管理

    (二)load()和asBitmap()

     在with()方法之后将返回RequestManager对象,那么接着我们直接去RequestManager的load(String)方法看看:

    RequestManager相关方法 RequestManager相关方法

    追着流程下去最后是进入了loadGeneric(Class modelClass)方法,该方法首先生成了两个ModelLoader对象,然后将这两个对象合着其他的内容传入了一个新的DrawableTypeRequest对象中,虽然最后的返回还调用了optionsApplier.apply(),但是我们知道Glide.with(context).load(url)返回的就是一个DrawableTypeRequest对象,所以我们大概可以判断最后就是返回这个新生成的DrawableTypeRequest对象了。

    这里的两个ModelLoader生成过程太过复杂就不分析,我们直接跟着主流程走下去

    debug

    但是我们可以通过断点知道这里获得的两个对象是StreamStringLoader和FileDescriptorStringLoader,接着我们看看optionsApplier是什么:

    RequestManager的构造方法

    在RequestManager的构造方法中optionsApplier由new了一个新的OptionsApplier对象赋值,那么我们看看OptionsApplier的apply方法:

    OptionsApplier debug

    调用options.apply之后就把传入的对象返回了,这个options我们从来没有设置过,同时在debug中它也确实为空,那么确实和我们猜测的一样,loadGeneric()中直接返回了new的DrawableTypeRequest对象,紧接着调用了它的load方法:

    DrawableTypeRequest继承关系 DrawableRequestBuilder GenericRequestBuilder

    可见load方法只是对model赋值,这里传入的model就是url,最后返回本身,那么Glide.with(context).load(url)也走完了,接着我们来看DrawableRequestBuilder的asBitmap()方法

    asBitmap()

    生成了一个BitmapTypeRequest对象并将DrawableRequestBuilder的相关配置传入且返回,所以整个Glide.with(context).load(url).asBitmap()还是比较清楚的,最后返回了一个BitmapTypeRequest对象

    (三)复杂的into()

    讲完了前几个步骤现在只剩下最后的的into()方法了,目前的几个步骤只是传递了参数,但是实际的图片请求毛都没见着,所以你应该可以预计into()方法做的事很多了,into()方法是在BitmapTypeRequest父类中实现的:

    BitmapTypeRequest继承关系 into()方法

    我们并没有做scaleType的配置,接着看glide.buildImageViewTarget(view, transcodeClass)

    buildImageViewTarget Glide类

    在Glide类中很清楚发现接着该找ImageViewTargetFactory.buildTarget()

    ImageViewTargetFactory

    ImageViewTargetFactory代码很简单,是作为一个工具类使用的,根据传递的class生成对应的ImageViewTarget,根据我们调用的Glide.with(context).load(url).asBitmap()其实我们可以猜测这里的clazz是Bitmap.class,我们还是去理一理怎么来的:

    into()方法 GenericRequestBuilder构造方法 BitmapTypeRequest构造方法

    看了构造方法我们可以发现BitmapTypeRequest确实传递了Bitmap.class进去,那么into()方法的传递参数是一个BitmapImageViewTarget对象

    into()方法

    在接下来into()方法中首先从target中取出之前的request,将之前的request回收掉,接着调用buildRequest(target)创建了一个新的request添加到requestTracker中,并且将target添加到lifecycle中,这列lifecycle对象就是一开始的ActivityFragmentLifecycle,那么这个request也将会跟着一开始添加的fragment生命周期运行了。在RequestTracker中,运行request其实是调用request.begin(),那么我们进入buildRequest(target)找找看生成的request这个方法是做了什么:

    buildRequest相关方法

    thumbnail相关是设置略缩图的,这里我们不用管,直接去看obtainRequest(target, sizeMultiplier, priority, parentCoordinator)

    obtainRequest()方法 GenericRequest.obtain()方法

    追踪下去可以看到最后生成的是GenericRequest对象,再接着看看begin()方法:

    begin()方法

    我们先看下面的代码:

    begin()方法部分代码

    如果是在正常状态,那么就调用target.onLoadStarted,那么我们回到BitmapImageViewTarget,onLoadStarted是在父类ImageViewTarget里重写的,我们看看干了什么:

    ImageViewTarget.onLoadStarted()

    所以是将我们传递给into()方法的ImageView设置了一张图片,原来我们在使用glide设置placeHolder图片就是在这个时候设置进来的了。再回到begin()方法,最上面是设置erorHolder的方法,只要model等于空,同样的设置错误站位图,这个model是什么呢,还记得我们在分析Glide.with().load()方法的源码吗:

    RequestManager.load() GenericRequestBuilder.load()

    传入的url最后传递给model,而这个model最后在生成GenericRequest的时候又传递给了它:

    obtainRequest()生成GenericRequest

    所以加载之前的判断条件是url是不是为空,为空则为异常。

    回到begin()方法,上面是异常,下面是默认展位图,那么中间就是加载图片的部分了:

    begin()方法部分代码

    Util.isValidDimensions判断是否设置了有效的overrideWidth和overrideHeight,我们没有设置过直接走else,target是BitmapImageViewTarget,在它的父类ViewTarget中实现了getSize()方法:

    ViewTarget.getSize() ViewTarget构造方法 SizeDetermine.getSize()

    依次追下去,我们发现最后调用的SizeDetermine.getSize(),currentWidth和currentHeight是展示加载的图片的ImageView的有效宽高,通常都可以获取到,那么我们直接去看cb.onSizeReady(currentWidth, currentHeight),这里传进来的cb也就是GenericRequest本身了。

    GenericRequest.onSizeReady()

    ImageView的宽高进来之后,计算了一个设置的宽高,因为默认sizeMultiplier是为1的,如果没有设置过sizeMultiplier默认设置宽高即为ImageView的宽高,之后的modelLoader和dataFetcher又得绕回去了,我们回去看看DrawableTypeRequest的构造方法:

    DrawableTypeRequest相关方法

    在DrawableTypeRequest的构造方法中调用了super构造方法,其中第三个参数传递了一个LoadProvider,这个provider就是之后传递到GenericRequest的loadProvider对象,buildProvider()方法一起贴出来了,返回了一个FixedLoadProvider对象,我们进入FixedLoadProvider发现getModelLoader返回的就是构造时传入的ImageVideoModelLoader对象,并且getTranscoder返回的也是构造时传递的transcoder,

    FixedLoadProvider部分代码

    我们跟着glide.buildTranscoder(resourceClass, transcodedClass)的流程,看看transcoder是什么:

    glide.buildTranscoder() transcoderRegistry.get()

    我们知道传递进来的decodedClass和transcodedClass分别是GifBitmapWrapper.class和GlideDrawable.class,那么这里最后会由factories.get(GET_KEY)返回结果,而在Glide类的构造方法中有一句这样的初始化代码:

    Glide构造函数部分代码

    在这里注册了GifBitmapWrapperDrawableTranscoder,那么最后返回就是GifBitmapWrapperDrawableTranscoder了,

    回到onSizeReady()方法,这些参数和其他一些配置都一起传递给了engine.load(),那我们找下engine是如何生成的:

    GenericRequestBuilder.obtainRequest()

    GenericRequestBuilder.obtainRequest()在创建GenericRequest的时候获取了glide的engine对象传递了进去,而glide初始化是通过GlideBuilder完成的:

    GlideBuilder.createGlide()

    在创建engine的时候,将cache和线程相关传递进去了,那么我们可以确定,图片的加载最后是交给engine处理了:

    Engine.load()方法

    在这个方法中首先根据配置生成了对应的key,之后是获取缓存的操作,我们先不看,因为还不知道是如何设置缓存的,我们直接看最下面的几段代码,首先根据key创建了一个EngineJob对象,其次根据配置选项创建了一个DecodeJob对象,随后将他们传入了一个EngineRunnable对象中,那么加载图片就是在这个runnable中执行了,调用了engineJob.start(runnable)之后就开始加载图片:

    EngineRunnable.run() EngineRunnable.decode()

    加载图片是通过decode()方法进行了,这里判断了是否要走缓存,我们直接不看缓存直接走decodeFromSource(),虽然EngineRunnable默认是走一次缓存的,但是第一次加载是没有缓存的,最后还是走decodeFromSource()

    EngineRunnable.decodeFromSource() DecodeJob.decodeSource()

    在DecodeJob中decodeSource()首先通过fetcher.loadData()获取data,这个fetcher是啥呢,我们重新回顾下DecodeJob的生成:

    GenericRequest.onSizeReady()部分代码 Engine.load()部分方法

    fetcher是这么传进来的,loadProvider.getModelLoader()返回的是ImageVideoModelLoader:

    ImageVideoModelLoader.getResourceFetcher()

    所以这里最后返回了一个ImageVideoModelLoader对象

    ImageVideoFetcher.loadData()

    随后进入了loadData()方法,其实在这里fileDescriptor获取为空,这是因为最开始初始化的原因,也是我一开始说的StreamStringLoader和FileDescriptorStringLoader生成太过复杂,我们只走主流程,streamFethcer是个OkHttpStreamFetcher对象,那么我们看看它的loadData()方法:

    OkHttpStreamFetcher.loadData()

    从OkHttpStreamFetcher名字可以看出,我所使用的这个版本的Glide是使用okhttp去加载图片,确实loadData里也是使用okhttp去加载的

    OkHttpUrlLoader$Factory.getInternalClient()

    在OkHttpUrlLoader$Factory中确实也初始化了OkHttpClient

    回到ImageVideoModelLoader,fetcher.loadData()返回的内容会封装在ImageVideoWrapper对象中回到DecodeJob返回,接着会把他传递到decodeFromSourceData()处理:

    decodeFromSourceData()

    我们没有做过磁盘缓存的配置,直接进入else判断,首先我们看loadProvider.getSourceDecoder()是什么:

    loadProvider.getSourceDecoder()

    这里的dataLoadProvider是初始化的时候传入的:

    FixedLoadProvider初始化 DataLoadProviderRegistry生成DataLoadProvider Glide初始化

    由上面的步骤可知,根据FixedLoadProvider初始化出入的class,生成的是ImageVideoGifDrawableLoadProvider对象,而ImageVideoGifDrawableLoadProvider.getSourceDecoder()则返回GifBitmapWrapperResourceDecoder:

    ImageVideoGifDrawableLoadProvider初始化

    那么接着看GifBitmapWrapperResourceDecoder.decode():

    decode() decode() decodeStream() decodeBitmapWrapper()

    GifBitmapWrapperResourceDecode的流程比较清楚,由于我们设置的是bitmap,按照流程下去最后会在decodeBitmapWrapper()返回了一个GifBitmapWrapper对象,这里还需要看看bitmapDecoder.decode(toDecode, width, height),虽然后面经过的类比较多,但是其实都只提供了一个方法去实现功能,那么这里我直接断点跟着流程进去了:

    ImageVideoBitmapDecoder

    bitmapDecoder是一个ImageVideoBitmapDecoder对象,其中又调用了streamDecoder.decode(is, width, height):

    StreamBitmapDecoder

    StreamBitmapDecoder再调用了downsampler.decode(source, bitmapPool, width, height, decodeFormat):

    downsampler部分代码

    Downsampler类中最后生成的bitmap就是调用BitmapFactory.decodeStream()获得的,这个bitmap会被封装进Resource对象返回,最后封装成回到EngineRunnable.run()返回,并传入onLoadComplete():

    EngineRunnable.run()

    这个结果会回传到EngineJob通过handler回调至handleResultOnMainThread()方法:

    EngineJob.handleResultOnMainThread()

    在这个方法中又将结果通过ResourceCallback回传,而在Engine.load()方法中我们将之前的GenericRequest添加进cbs了,所以这里又会回到GenericRequest:

    Engine.load() GenericRequest.onResourceReady()

    在onResourceReady()方法中调用了target.onResourceReady(result, animation),还记得我们在调用into(ImageView)方法时ImageView被封装进了BitmapImageViewTarget吗,这里的target就是它了,:

    BitmapImageViewTarget.setResource()

    终于,到这里图片被加载下来并设置上view了!

    回头看看Engine.load()中之前遗留下来的的东西:

    Engine.load()

    这三个处理分别从cache、activeResources、jobs去取内容,我们在获取到bitmap传递内容的时候封装的Resource对象会又被封装进EngineResource在Engine之间传递,在EngineResource中有个释放资源的方法,里面的listener就是Engine:

    EngineResource.release() Engine.onResourceReleased()

    也就是在释放资源的时候内存中做了缓存,而activeResources是保存当前活跃的Resource,分别会在Complete()和获取cache的时候保存Resource,Released()的时候移除Resource:

    保存Resource 保存Resource 移除Resource

    jobs也类似,在任务完成或者取消的时候会移除job,如果发起的任务已有就不重复发起:

    移除job时机

    现在看来,Glide.with(context).load(url).asBitmap().into(imageView)这一行代码居然做了这么多的操作,而且我们还只是走了主线,但是其中很多的技巧已经很值得让人学习了。

    相关文章

      网友评论

        本文标题:Android Glide浅析

        本文链接:https://www.haomeiwen.com/subject/litdvftx.html