基本概念
上一部分我们分析了Glide的生命周期管理,到这一步时RequestManager已经构建完成了,接下来就该依照外界传入的配置构建具体Request发出加载请求了,也就是终于到了真正干活的地方了。Glide的优秀之处(也是为什么有那么多代码)的原因就是在这个部分扩展性很强,你甚至可以把Glide完全改造,包括具体的网络请求、请求的数据类型、对最终数据的操作都可以重写,只让Glide处理线程调度和成名周期绑定,而不只是加载图片和gif。
请求流程图

相关类的介绍
RequestManager:用于接受外界传入的uri根据数据类型生成GenericRequestBuilderGenericRequestBuilder:用于构建具体用于数据请求的RequestRequest:携带一个具体请求相关的所有信息Engine:当Request被执行时,有Engine负责管理总体的数据请求流程EngineRunnable: 实现了Runnable,有这个类持有EngineJob和DecodeJob在子线程进行原始数据请求EngineJob: 负责开启数据请求及将结果通过EngineJobListener和ResourceCallback回调给EngineJob和具体的RequestDecodeJob: 真正执行数据请求的类,向外界暴露从缓存中请求和从数据源请求原始数据的方法
源码分析
与之前一样,这个过程只看只看类的作用只能看个大概,还是需要搬出来源码解释问题。我们以Glide.with(context).load(url).into(imageview)为例,首先看RequestManager中的相关方法:
1 |
|
这部分源码比较抽象,但是做的事情并不复杂,就是拿到传入的数据后,根据数据类型构建一个ModelLoader,这个ModelLoader的作用后面讲。然后再拿着这个ModelLoader构建一个DrawableTypeRequest,这个DrawableTypeRequest是GenericRequestBuilder的子类,然后看它的相关源码:
1 |
|
尴尬,好像是一个建造者模式,只是传入了数据,并没有进行操作,但是也可以知道,Glide.with(context).load(url).into(imageview)这个完整请求的最后一步,into()方法肯定也在这个类里面,那我们继续看相关源码:
1 |
|
首先是into(imageview)这个方法,无视无用信息,关键代码只有最后一句,执行的是into(target)方法。然后我们继续看这个方法,这时候很明显了,在into(target)执行时,会构建一个Request,然后被执行runRequest。再看runRequest方法的源码:
1 |
|
很简单,本质上就是执行Request的begin()方法。Request是一个接口,我们看它的主要实现类GenericRequest的源码:
1 |
|
这部分代码如果想要完全搞清楚就必须翻源码了,基本流程是begin()方法被执行之后,会等待onSizeReady的回调执行,这句不用看也知道意思是要等View被绘制出来拿到具体尺寸之后,才真正发起请求,因为Glide在加载图片的时候都会根据View的尺寸对源数据进行压缩,缓存也只会缓存这种数据(任何图片库都一样,毕竟内存吃紧)。在onSizeReady毁掉中,我们只看关键代码,其实就是Engine的load()方法,我们再看这部分源码:
1 |
|
这个方法比较长,但是只看方法名的话也完全可以理解,先从内存缓存中取数据,没有命中就从activeResources中取,还没有命中就发起一个EngineRunnable请求源数据,EngineRunnable会持有一个EngineJob和一个DecodeJob,我们依次来看它们的作用:
1 |
|
贴代码的时候才发现这两部分必须一起分析···先看EngineJob,它内部有两个ExecutorService,很明显一个是用于请求磁盘数据,另一个用于请求源数据。EngineJob实现了EngineRunnableManager这个接口,它只是用来管理这两个线程池的相关操作,同时通过持有的ResourceCallback和EngineJobListener将结果回调给Request和Engine,线程具体的run()方法在我们上面贴出的EngineRunnable里面,然后我们再看这部分代码,本质上就是通过decode()方法进行数据请求,如果属于请求磁盘数据且失败了,那么就去请求源数据,最终将结果回调给EngineJob。那么关键就是这个decode()方法了:
1 |
|
这部分代码只看方法名也很清晰,就是通过持有的DecodeJob分别从缓存(磁盘缓存)和数据源去取数据,然后就引出了真正干活的DecodeJob了:
1 |
|
这部分代码比较多,但是也基本就是取源数据的全部了。那么以一个网络请求数据源为例,这部分代码在哪里呢?就在decodeSource()里的final A data = fetcher.loadData(priority);这一句了,这个DataFetcher的所有实现类,就是从所有路径取请求源数据的实现了,我们以OkHttp对应的OkHttpSteamFetcher为例:
1 |
|
用过OkHttp的同学都知道,这是OkHttp最基本的一个请求,通过这个请求,我们也通过传入的路径拿到了我们需要的数据流。到这时候,一个完整的请求就基本结束了。
不过还没完,因为数据拿到了,也处理完了,还没显示在View上,这时候就要回头看Request拿到的请求结果的回调里做了什么了,还是以实现类GenericRequest为例:
1 |
|
与我们预期的一致,请求结果被传递给Request对应的target的onResourceReady方法,在这个方法里完成了最终数据与View的绑定工作。到这时候,一个完整的图片加载过程就完成了。
总结
当然,我们这一篇的分析真是跟着源码把数据请求的过程走了一遍,其中还有许多有意思的设计并没有设计。比如在Target的实现类ViewTarget中,会通过view.setTag()方法实现与Request的绑定,避免了同一个View的重复请求;通过抽象出一个ModelLoader和DataFetcher使使用者能够任意自定义底层数据请求的方式;通过抽象出Transformation使使用者能够在拿到最终数据之前进行自定义的处理。个人认为Glide优于Picasso的地方也在这里,就是极致的可扩展性。这部分原理靠文字是无法完全描述清楚的,还是需要从源码中寻找答案。