前言
之前有写Glide的流程和一些重要类效果的相关文章,不清楚Glide全体流程和重要类的效果的建议先看之前的文章。
Glide 作为一个像Android SDK 相同的第三方图片结构,久经各大项目的考验,read the fuck glide source code ,深入其架构思维,规划形式,关于进步咱们的编程才能是无可厚非的 。
非标题党直接步入主题,先从创造型规划形式讲起。
创造型规划形式
单例形式
volatile + 两层校验办法 ,确保Glide 在整个应用只有一个实例 。运用volatile 防止指令重排序(new 目标包括分配内存,初始化,引用赋值 三条指令) ,由于可能取得一个未初始化完成的Glide , Glide 引用尽管不为空 ,可是里边的变量由于没有初始化所以可能为null ,这种状况运用就会出问题。两层校验办法确保glide 只能 new 一次。
工厂形式
将实例的构建和运用分离隔,下降代码耦合度,能够完成目标的灵敏装备 , 封装目标的创立逻辑,简化目标的运用,供给目标的复用和办理,说了这么多,咱们来看下Glide怎样用的。
- 简单工厂
glide 经过工厂构建不同类型的线程池,很好理解。
- 工厂办法
每一个详细实例对应一个详细工厂
比方:用来感知网络连接状况的接口ConnectivityMonitorFactory和 ConnectivityMonitor,和高层RequestManager。
ConnectivityMonitorFactory在Glide 初始化被构建,由于ConnectivityMonitorFactory生产的ConnectivityMonitor构建需求依靠 android 中的Context 上下文,context在 Glide 初始化中传入。 ConnectivityMonitor在RequestManager中运用,咱们不可能把context 传到RequestManager中去吧?RequestManager只用于办理恳求,不需求耦合android context 相关的,这样才契合单一职责准则,所以咱们需求一个ConnectivityMonitorFactory工厂,RequestManager 作为Client 要运用ConnectivityMonitor, 只需求从ConnectivityMonitorFactory工厂中取得就行了。
从依靠倒置准则来看高层RequestManager 只依靠ConnectivityMonitorFactory和 ConnectivityMonitor笼统,不依靠详细完成细节,也是契合依靠倒置准则的。从接口阻隔准则来看,RequestManager 需求感知网络状况只依靠ConnectivityMonitor和其工厂类,没有多余的接口依靠,也契合接口阻隔准则。从里氏替换准则来看,ConnectivityMonitor 和ConnectivityMonitorFactory一切完成子类都能够出现在RequestManager,也契合里氏替换准则。从迪米特规律来看,RequestManager需求感知网络变化,需求用到context ,但又不是直接和context耦合,凭借ConnectivityMonitor 来完成,也契合迪米特规律。
构建者规划形式
为什么要运用构建者规划形式 ?支撑链式调用,防止结构参数列表过长,进步结构目标的灵敏性和可装备性,使代码愈加可保护
如GlideBuilder
运用Builder 形式,简化Glide 的创立,优点清楚明了不多解说了。
原型形式
经过原型形式,咱们能够根据一个现有目标创立出新的目标,而且能够灵敏地修正新目标的属性,而不会影响原始目标。这样能够防止重复创立相似的目标,进步功用和代码复用性。克隆细分又分深拷贝和浅拷贝。听君一席话,如听一席话,咱们看下Glide怎样做的。
Glide 哪里用到了原型形式呢? 在构建恳求目标的恳求参数就用到了,如下。
为什么要用clone,对应什么场景?其实就这个问题我也考虑良久,让咱们自己写图片加载结构估计就直接new 恳求参数,然后把恳求参数都塞进去,又不是不能用,这样确实是能用而且一点问题都没有。可是作为一个有梦想的程序员肯定是代码能优化当地就要优化的。例如下面这个场景
把一张图片加载到多个imageView 上面去,这种需求还是很常见的。这种状况下恳求参数大部分信息都是能够共用的,就imageView不相同而已,我clone 一个出来复用大部分相同的恳求参数信息,功用和内存不都优化了?何乐而不为,然后不同的部分 deep copy一下就行了。
行为型规划形式
观察者规划形式
glide 经过添加透明Fragment来监听activity /framgent 生命周期,这儿提一下Glide为什么在子线程运用不会去监听生命周期,由于可能会导致内存走漏。在生命周期onDestroy 整理加载恳求、onStop加载暂停、onStart康复图片加载恳求,用到了观察者规划形式。与之相关两个接口界说如下
LifecycleListener 是笼统观察者,办法有onStart/Stop/Destory,Lifecycle是笼统被观察者,办法有add/removeListener。RequestManager 作为详细观察者,完成了LifecycleListener。除了RequestManager完成了观察者接口是观察者外,还有ConnectivityMonitor ,TargetTracker,Target 都承继/完成LifecycleListener都是观察者,观察者比较简单不多讲了。
接下来讲讲glide 核心代码所用的职责链规划形式,经过职责链规划形式使多个目标都有机会处理恳求,然后防止恳求的发送者和接收者之间的耦合联系。将这些目标连成一条链,并沿着这条链发送该恳求,直到有一个目标处理它停止。
职责链规划形式
如下 , ResourceCache(不需求解码和变换直接能够显现) ,DataCache(需求解码和变换) , Source 别离对应三个Generator,前两个从本地磁盘缓存中获取的图片数据,后边则直接从源(网络或许app本地资源文件中)Glide 按照装备依次从中获取图片加载数据。
这是职责链形式?怎样看都不像啊,职责链界说不是要将这些目标连成一条链,并沿着这条链发送该恳求,直到有一个目标处理它停止。也就是说职责链形式都要有恳求参数,就glide 而言这儿给Generator的恳求参数decodeHelper早在Generator初始化就传入了,就不用在startNext()办法中传入decodeHelper了。
代码都是死的,可是思维都是相同的,其实上面的代码也能够改成标准职责链形式。职责链规划形式有什么优点?职责链规划形式能够解耦恳求发送者和接收者,简化目标之间的交互,供给灵敏的处理顺序,使得系统愈加灵敏、可扩展和易于保护。它适用于需求动态组合和处理多个相关目标的场景,特别是在处理杂乱的事务逻辑时,能够将杂乱性分解成简单的处理进程。
就Glide 而言,将解码恳求发送者DecodeJob 高层目标和各种解码详细处理目标ResourceCacheGenerator等解耦,以及详细恳求目标之间都经过职责链形式解耦了,DecodeJob作为高层目标无需重视解码细节,只需重视解码的全体流程就行。除了解耦之外还供给良好的扩展性,比方不需求从磁盘缓存中获取已解码的图片缓存, 只需求去掉return new ResourceCacheGenerator就行了,如下。
战略形式
glide 用于判别支不支撑从磁盘缓存中获取图片就用到了战略形式
DiskCacheStrategy 有几个子类ALL 表示全部支撑, NONE 什么都不支撑。默许运用 AUTOMATIC 只有从网络才支撑Data 缓存,而且从网络获取中假如运用了Data 缓存则不支撑ResourceCache。咱们要改缓存战略的话能够在恳求参数中修正DiskCacheStrategy。
另外还有解码的时分向下采样也用到了战略形式,根据imageView 的scaleType 别离对应了几种战略。
结构型规划形式
装修形式
经过装修者形式咱们能够在运行时动态地给目标添加额定的功用,而不需求改动原始目标的结构。这使得咱们能够灵敏地组合目标并添加所需的功用,然后满足不同的需求,接下来咱们看下glide 中怎样运用的。
Glide 从moedl -> data 这个进程就用到了装修者规划形式,与之相关的接口是 ModelLoader<Model, Data> 以及完成此接口的子类。假设model 为string状况 ,这种状况对应的有表示网络恳求的url ,本地文件 uri ,asset uri ,图片文件路径等,此刻ModelLoader 详细完成类为StringLoader。
StringLoader 为被装修者,结构中传入装修者uriLoader ,uriLoader装修者给StringLoader动态扩展功用,但又不会修正和影响StringLoader全体结构。uriLoader给StringLoader 扩展了什么功用呢?比方支撑从网络中获取图片的HttpGlideUrlLoader ,支撑从ContentResolver 获取图片的UriLoader,以及支撑从 Asset目录下获取图片的AssetUriLoader等等。假如不运用装修者规划形式,把这些功用完成都写在StringLoader 中,可想而知StringLoader 中代码量有多大,有多难保护了。
除了装修规划形式其实ModelLoader还用到了组合规划形式。
组合规划形式
前面提到StringLoader 要动态新增功用运用了装修规划形式,考虑一下假如新增那么多功用是不是要给每个功用都树立一个装修类?假如一两个功用还好,可是类多的话就爆破了,有没有更好的办法去处理呢?
glide运用组合形式来规避了这个问题,引入一个MultiModelLoader ,望文生义这个里边能够放许多个ModelLoader,把那些装修StringLoader的装修者都放在MultiModelLoader里,MultiModelLoader 把这些装修者组合起来,这样能够防止类爆破的问题。
有些人会以为你把这些装修者都放一起这不是严重耦合?不契合六大规律啊,程序规划讲究高内聚低耦合,你这耦合这么严重以后怎样保护啊,你这代码不保熟啊。
看代码其实就知道并不是这样的,MultiModelLoader仅仅依靠了各种装修者的笼统,并没有依靠各个装修者的详细完成,详细装修者的代码修正彻底不影响MultiModelLoader 。从依靠倒置准则来看,MultiModelLoader 作为client 高层目标依靠笼统ModelLoader ,各个详细装修者如HttpGlideUrlLoader 也依靠笼统ModelLoader ,MultiModelLoader和HttpGlideUrlLoader经过ModelLoader 笼统完成了依靠倒置。从里氏替换准则来,ModelLoader笼统能够出现的当地,ModelLoader的各种子类装修者也能够出现在MultiModelLoader中。从迪米特规律和单一职责来看,MultiModelLoader只用于办理统筹多个ModelLoader,不耦合详细完成 。
享元规划形式
每次去加载图片都要一个DecodeJob 去履行图片加载任务,假如一段时间内履行成百上千个图片加载恳求,有可能会造成内存颤动,有没有能够优化的当地呢?glide运用享元形式来复用DecodeJob目标,优化和节约内存的运用,如下。
在DecodeJob release的时分将其放入池子中回收。
glide 中不止DecodeJob目标用了享元形式来复用目标优化内存, 还有许多目标比方EngineJob,Bitmap等都是用池子来复用的。
这儿提出个问题,为什么每次去加载图片根本都会新建一个Request ,为什么Request 没有用享元形式呢?
glide 中用到的规划形式还有优异的规划思维远远不止此文所述,一篇文章终究是难以说清,但学习办法都是相同的,就是read the fuck glide source code ,把glide 的皮一层一层拨开。
最后,给自己打个广告!
求职求职求职!!!
个人简介:
两年半 + 两年半 经验老安卓 ,安卓原生开发 / NDK 开发/ SDK 开发/ 逆向 / flutter 都有涉猎。
可内推的大佬们费事直接在直接私聊我!
谢谢!!