基本概念
上一部分我们分析了Glide
的生命周期管理,到这一步时RequestManager
已经构建完成了,接下来就该依照外界传入的配置构建具体Request
发出加载请求了,也就是终于到了真正干活的地方了。Glide
的优秀之处(也是为什么有那么多代码)的原因就是在这个部分扩展性很强,你甚至可以把Glide
完全改造,包括具体的网络请求、请求的数据类型、对最终数据的操作都可以重写,只让Glide
处理线程调度和成名周期绑定,而不只是加载图片和gif。
请求流程图
相关类的介绍
RequestManager
:用于接受外界传入的uri根据数据类型生成GenericRequestBuilder
GenericRequestBuilder
:用于构建具体用于数据请求的Request
Request
:携带一个具体请求相关的所有信息Engine
:当Request
被执行时,有Engine
负责管理总体的数据请求流程EngineRunnable
: 实现了Runnable
,有这个类持有EngineJob
和DecodeJob
在子线程进行原始数据请求EngineJob
: 负责开启数据请求及将结果通过EngineJobListener
和ResourceCallback
回调给EngineJob
和具体的Request
DecodeJob
: 真正执行数据请求的类,向外界暴露从缓存中请求和从数据源请求原始数据的方法
源码分析
与之前一样,这个过程只看只看类的作用只能看个大概,还是需要搬出来源码解释问题。我们以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
的地方也在这里,就是极致的可扩展性。这部分原理靠文字是无法完全描述清楚的,还是需要从源码中寻找答案。