Glide 4.x添加自定义组件原理
在《Glide 4.x之ModelLoader简单分析》(在阅读本篇博客之前,强烈建议先读这篇博客,否则有点上下文不衔接的感觉)解析我们知道了ModelLoader的一些工厂类都通过都是通过Registry这个类来登记注册的,且通过我们的怼ModelLoader的讲解,我们知道了Glide访问网络数据是通过UrlConnection,且其核心配置为
registry.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
但是开发过程中我们想要使用别的网络库怎么办呢?在我们实际的项目中用的Glide经常会遇到timeout的问题导致图片加载不出来,当时用的Glide3.7.0,看了其源码发现该版本的超市时间时候写死的2500ms,对外也没有提供超时的时间接口,而且我手头项目中使用的是OKhttp来发起网络请求,且我们在项目中对OKttp做了二次封装,为此我们就使用okhttp来发起glide的图片请求。
因为项目中使用的是3.x的版本,先从3.x版本分析,然后还回到4.2.0版本进行分析,不过在这里先提前说一句Glide3.x版本通过实现GlideModule接口来添加自定义组件,而Glide4.x版本则摒弃了GlideModule接口转而使用LibraryGlideModule接口,具体的后面在说明。
3.X版本集成Okhttp的方法步骤
Glide集成Okhttp的方法很简单也就两步(详细见官网)
1、在Gradle配置配置如下一句:
dependencies {compile 'com.github.bumptech.glide:okhttp3-integration:1.5.0@aar'
}
2、在AndroidMainfext.xml添加如下代码:
<meta-dataandroid:name="com.bumptech.glide.integration.okhttp.OkHttpGlideModule"android:value="GlideModule" />
看完这两个步骤,其实思路也很简单,无非就是在初始化的时候解析AndroidManifest.xml文件将OkHttpGlideModule加载进来,观察glide初始化代码就可以知道。Glide 3.7.0的初始化Glide方法如下:
public static Glide get(Context context) {if (glide == null) {synchronized (Glide.class) {if (glide == null) {//1、解析清单文件获取GlideModule集合List<GlideModule> modules = new ManifestParser(applicationContext).parse();//2、初始化构建者模式对象GlideBuilder builder = new GlideBuilder(applicationContext);//3、循环集合调用GlideModule的applyOptions方法for (GlideModule module : modules) {module.applyOptions(applicationContext, builder);}//4、创建glide对象glide = builder.createGlide();//5、注册自定义组件for (GlideModule module : modules) { module.registerComponents(applicationContext, glide);}}}}return glide;}
上面的代码核心逻辑就是解析AndroidMainfest.xml文件获取用户配置的GlideModule对象集合,然后遍历GlideModule集合,先运行GlideModule接口的applyOptions方法往builder里面构建自己的组件,而后调用registerComponents方法来将组件注册到glide里。
以上文提到的OkHttpGlideModule为例,其源码为:
public class OkHttpGlideModule implements GlideModule {@Overridepublic void applyOptions(Context context, GlideBuilder builder) {// 此处什么都没做,冗余空方法.}@Overridepublic void registerComponents(Context context, Glide glide) {//注册使用Okhttp发起网络访问glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());}
}
如果阅读了博主的《Glide 4.x之ModelLoader简单分析》文章的话不难理解上面registerComponents方法都做了些什么,此处就不在赘述。也就是说Glide3.x版本让客户端实现GlideModule接口来添加自己的组件,该接口也很简单:
public interface GlideModule {//该方法先执行void applyOptions(Context context, GlideBuilder builder);//该方法后执行void registerComponents(Context context, Glide glide);
}
applyOptions就是参数里面有一个GlideBuider对象,构建者对象的作用不言而喻,我们可以通过GlideBuider对象来添加自己的配置。此方法执行过后Glide对象并没有初始化出来,在执行该方法过后如上面get方法所以会初始化glide,然后将初始化的glide传递给registerComponents方法执行,这样我们就可以操控glide对象添加自己想添加的东西了。也就是说applyOptions允许我们操控GlideBuilder对象,通过构建者模式不断构建我们所需的组件,而registerComponents允许我们直接操控glide对象来完成一些操作,比如上面okhttp的集成。
在此我提一个问题:必须通过配置android MainFest文件来完成上述的操作吗?答案当然不是的,因为对于插件话的开发来说很可能没有MainFest.xml文件(博主在和客户对接SDK的时候就遇到过这种问题,博主开发中使用的版本是3.7,0),而get方法又要对清淡文件进行扫描解析,因为没有清单文件导致调用get方法的时候报错,app直接崩溃。解决这个方法很简单,遍静态注册为动态注册,初始化Glide的代码在get方法之前完成,下面几行代码就搞定了没有清单文件,而又想使用Okhttp的操作:
GlideBuilder builder = new GlideBuilder(context);
/*初始化Glide的单利对象*/
Glide.setup(builder);OkHttpUrlLoader.Factory factory = new OkHttpUrlLoader.Factory();
/*此时调用get方法因为glide对象已初始化,不会再走
get方法内部的初始化,从而避免了对清单文件的扫描
*/
Glide.get(context).register(GlideUrl.class, InputStream.class, factory);
不过从Okhttp的集成来看,GlideModule这个接口的设计并不是很合理或者说没有做到单一职责,因为我可能只需要对GlideBuider进行操作,而不需要直接操作Glide。换句话说就是我可能只需要applyOptions方法就行了,不需要registerComponents。也可能是只需要registerComponents不需要applyOptions。从OkHttpGlideModule来看我们只需要registerComponents,而根本不需要applyOptions方法,造成applyOptions的冗余。这一设计的缺陷在在Glide4.X的时候得到的改正:
Glide4.X 版本添加自定义组件
4.x版本使用需要如下配置:
dependencies {compile 'com.github.bumptech.glide:glide:4.3.1'annotationProcessor 'com.github.bumptech.glide:compiler:4.3.1’
}
此处还是以集成OKhttp来说明情况,如果需要集成Okhttp的话还需要添加一句:
compile "com.github.bumptech.glide:okhttp3-integration:4.3.1”
是不是感觉so easy?在Okhttp工作的地方打个断点,debug一把发现glide不想跟我“说话”,并且丢给了我一行bug:
Failed to find GeneratedAppGlideModule. You should include an annotationProcessor compile dependency on com.github.bumptech.glide:compiler in your application and a @GlideModule annotated AppGlideModule implementation or LibraryGlideModules will be silently ignored
排查发现经过在Glide类的getAnnotationGeneratedGlideModules方法在找不到GeneratedAppGlideModuleImpl类时会有这个异常log输出:
try {Class<GeneratedAppGlideModule> clazz =(Class<GeneratedAppGlideModule>) Class.forName("com.bumptech.glide.GeneratedAppGlideModuleImpl");result = clazz.newInstance();
} catch (ClassNotFoundException e) {if (Log.isLoggable(TAG, Log.WARN)) {Log.w(TAG, "Failed to find GeneratedAppGlideModule. You should include an"+ " annotationProcessor compile dependency on com.github.bumptech.glide:compiler"+ " in your application and a @GlideModule annotated AppGlideModule implementation or"+ " LibraryGlideModules will be silently ignored");}
GeneratedAppGlideModule是一个抽象类,且需要通过类名来获取GeneratedAppGlideModuleImpl的对象,但是在glide的包里面并没有找到这个类,然后Google了一会儿还没搜到解决方案,但是我突然想到上面的gradle里面配置了annotationProcessor这个家伙(关于这个的简单说明可以参考此篇博文然后就知道大致原因了,然后看官网有这么一段配置:
果断应用的一个包里面添加MyAppGlideModule这个类,然后rebuild一下项目果然不出所料GeneratedAppGlideModuleImpl出来了,如图:
此时在请求一次http图片请求,果然走的是OKhttp的逻辑,下面就来抽丝拨茧,说下其内部的原理:
看看GeneratedAppGlideModuleImpl的源码发现我们定义的MyAppGlideModule已经作GeneratedAppGlideModuleImp类的一个引用在里面了。该上文讨论3.X的时候我们为了不扫描Manifest.xml文件不得不改变Glide的初始化方式,也就是说如果使用3.x默认的初始化方式的话无论如何都避免不了清单文件的扫描,但是这种情况在4.x中得到了改善,解决方法就是在GeneratedAppGlideModuleImpl类里面有一个isManifestParsingEnabled方法,如果改方法返回了false则不对Mainfest文件进行扫描解析:
@Overridepublic boolean isManifestParsingEnabled() {return appGlideModule.isManifestParsingEnabled();}
在详细GeneratedAppGlideModuleImpl之前有必要说说GeneratedAppGlideModuleImpl继承关系(偷个懒不画UMLl了):
GeneratedAppGlideModuleImpl->AppGlideModule->LibraryGlideModule—>AppliesOptionsLibraryGlideModule—> RegistersComponents
还记得上文说到GlideModule这个接口设计的不合理吗,在4.x版本就将3.x的GlideModule接口拆分为两个接口RegistersComponents和AppliesOptions。根据上面对3.x的讲解我们来重点看看GeneratedAppGlideModuleImpl的applyOptions和registerComponents方法:
public void applyOptions(Context context, GlideBuilder builder) {appGlideModule.applyOptions(context, builder);
}
该方法直接调用appGlideModule.的applyOptions方法,此方法可以为空,但是此方法清楚的告诉我们一个信息:我们可以直接操控GilideBuilder对象搞事情:比如通过builder对象来配置自己的缓存策略等。
下面来分析GeneratedAppGlideModuleImpl 对象的第二个重要方法registerComponents
public void registerComponents(Context context, Glide glide, Registry registry) {//配置OKhttp组件new OkHttpLibraryGlideModule().registerComponents(context, glide, registry);//配置其他的插件appGlideModule.registerComponents(context, glide, registry);
}
如上所示上面代码显示了配置OKhttp的地方,如果想配置Okhttp则需要调用OkHttpLibraryGlideModule的registerComponents方法,所以让我们看看该方法都做了些啥:
public final class OkHttpLibraryGlideModule extends LibraryGlideModule {public void registerComponents(Context context, Glide glide, Registry registry) {//添加自己的网络请求库registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());}}
直接调用了registry的replace方法,有关Registry类的初步说明情参考上篇博文,上面调用了replace方法,那么replace了谁呢?本篇博文开头说了Glide发起网络请求的配置是就是:
append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
那么我们有理由判断这个replace替换的就是HttpGlideUrlLoader.Factory这个东东。用代码来说话,看看replace方法:
public <Model, Data> Registry replace(Class<Model> modelClass, Class<Data> dataClass,ModelLoaderFactory<Model, Data> factory) {modelLoaderRegistry.replace(modelClass, dataClass, factory);return this;
}
然后调用了modelLoaderRegistry的replace方法,如果不明白modelLoaderRegistry,可以参考这个图片:
public synchronized <Model, Data> void replace(Class<Model> modelClass, Class<Data> dataClass,ModelLoaderFactory<Model, Data> factory) {//1、调用multiModelLoaderFactory的replace方法List<ModelLoaderFactory<Model, Data>> factories = multiModelLoaderFactory.replace(modelClass, dataClass, factory)//2、遍历factories集合tearDown(factories);cache.clear();
}
上面的代码调用了multiModelLoaderFactory对象的replace方法,继续深入之:
synchronized <Model, Data> List<ModelLoaderFactory<Model, Data>> replace(Class<Model> modelClass,Class<Data> dataClass, ModelLoaderFactory<Model, Data> factory) {//根据modelClass和dataClass来匹配删除glide本身注册factory对象//因为remove的逻辑比较简单,就不在贴具体实现的源码List<ModelLoaderFactory<Model, Data>> removed = remove(modelClass, dataClass);//添加我们自定义的factory对象append(modelClass, dataClass, factory);return removed;
}
到此为止,Okhttp的组件库已经成功的添加到了glide中,但是什么时候开始扫描GeneratedAppGlideModule的呢?肯定是初始化Glide的时候开始扫描,要不然图片都加载出来了在扫描添加Okhttp还搞毛,所以我们看看Glide的初始化方法initializeGlide:
private static void initializeGlide(Context context) {Context applicationContext = context.getApplicationContext();/*根据注解获取GeneratedAppGlideModuleImpl对像*/GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();/*是否扫描Mainfest.mxl*/if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {manifestModules = new ManifestParser(applicationContext).parse();}
/*省略大量代码*/if (annotationGeneratedModule != null) {/*执行GeneratedAppGlideModuleImpl的applyOptions*/ annotationGeneratedModule.applyOptions(applicationContext, builder);}/*初始化Glide*/Glide glide = builder.build(applicationContext);/*省略部分代码*/if (annotationGeneratedModule != null) {/*真正添加自己的组件的地方,比如OKhttp*/ annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);}}
上面的代码去掉与本文无关的逻辑之后主要是:
1、获取GeneratedAppGlideModuleImpl对象,该对象的产生过程见上文
2、执行GeneratedAppGlideModuleImpl的applyOptions方法
3、执行GeneratedAppGlideModuleImpl的registerComponents将Okhttp等自定义的组件添加到glide中。
其实还有一个更简单的添加自定义组件的方法,根本不用配置GeneratedAppGlideModuleImpl或者扫描Mainfest.xml这么麻烦,只需要如下四个步骤即可:
1、创建GlideBuider对象:
GlideBuilder builder = new GlideBuilder();
2、创建Glide对象
Glide glide = builder.build(applicationContext);
3、因为Glide完成初始化的时候Registry对象也初始化完毕,并且我们可以通过glide.getRegistry()获取到该对象,所以通过操作glide对象直接添加Okhttp等自定义组件:
glide.getRegistry().replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
4、最后别忘了最重要的一句:
Glide.init(glide)
其实不论时通过注解还是上面的三个操作步骤都是殊途同归,其内部原理就是操作Registry对象,然后将自定义组件通过Registry提供的append或者replace等方法来添加而已。
到此位置,本篇博文结束,整体感觉有点啰嗦,不当之处欢迎批评指正,共同学习。
Glide 4.x添加自定义组件原理相关推荐
- SpingMVC框架:fileUpload组件原理和实现
QUESTION:fileUpload组件原理和实现 ANSWER: 目录 QUESTION:fileUpload组件原理和实现 ANSWER: 一:异常产生 查询了一系列博客后,发现这是由于上传文件 ...
- SpringCloud_004_SpringCloud服务发现组件原理介绍
SpringCloud_004_SpringCloud服务发现组件原理介绍 技术交流qq群,交流起来方便一些:170933152 1.如何解决硬编码问题? 上次咱们说到,硬编码问题 比如: 服务发现组 ...
- QGC添加自定义组件和发送自定义MAVLINK消息
QGC添加自定义组件和发送自定义MAVLINK消息 一.添加自定义组件 1.1 在飞行界面添加组件 1.2 实现组件事件 1.3 在MOCK模拟链接中实现验证 1.4 验证 二.自定义MAVLINK消 ...
- Flutter 动画全解析(动画四要素、动画组件、隐式动画组件原理等)
本文通过拆解 Flutter 中动画的实现方式以及原理来介绍动画实现的整个过程. 1. 动画四要素 动画在各个平台的实现原理都基本相同,是在一段时间内一系列连续变化画面的帧构成的.在 Flutter ...
- springboot继承组件_SpringBoot如何扩展引入的组件,以及如何自动配置组件原理
大家都知道,当我们创建SpringBoot项目之后,我们可以在pom文件下,引入我们想要启动的组件,当我们引入之后,SpringBoot会自动帮我们配置属性! 下面我们以SpringBoot引入Spr ...
- Spring Cloud一些组件原理(一)
Spring Cloud的一般组件: Eureka:注册中心,这个必须要放在第一个说,毕竟没有注册中心就没有服务的自动伸缩,就谈不上微服务了: Ribbon.Feign:远程调用,没有远程调用也还是谈 ...
- Universal-Image-Loader,android-Volley,Picasso、Fresco和Glide五大Android开源组件加载网络图片的优缺点比较
在android中的加载网络图片是一件十分令人头疼的事情,在网上有着许多关于加载网络图片的开源库,可以让我们十分方便的加载网络图片.在这里我主要介绍一下我自己在使用Volley, Picasso, U ...
- Kubernetes——K8s架构与组件原理
摘要 通过现代的 Web 服务,用户希望应用程序能够 24/7 全天候使用,开发人员希望每天可以多次发布部署新版本的应用程序. 容器化可以帮助软件包达成这些目标,使应用程序能够以简单快速的方式发布和更 ...
- Netty异步非阻塞事件驱动及组件原理详解
本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件.整体架构,知其然且知其所以然,希望给大家在实际开发实践.学习开源项目方面提供参考. Netty 是一个异步事件驱动的网络应用程序 ...
- 你真的完全掌握了 Input 组件的键盘控制么? ——百度智能小程序 Input 组件原理剖析与键盘行为说明
在百度智能小程序的很多开发场景中,我们都会使用到 Input 输入框组件.而在操作输入框的过程中,正确处理键盘的弹出和收起行为也是十分重要的一环.键盘行为不仅需要完美符合业务场景,同时也和用户体验息息 ...
最新文章
- 现代软件工程 第十章 【典型用户和场景】 练习与讨论
- Linux运维工程师可是很吃香的
- CentOS下如何完全卸载MySQL?解决卸载不干净的问题
- WebService服务发布与使用(JDK自带WebService)
- Laravel 深入核心系列教程
- 防碰撞算法 matlab仿真,基于毫米波雷达的汽车防撞系统的设计
- php opendir(),php之opendir()函数的用法
- jeecgboot修改登录界面、背景图等的页面记录
- 最新版PandoraBox潘多拉安装adbyby去广告插件图文详细教程!!
- 较新颖的智能优化算法
- 矩阵的分解——LU分解
- ipv6访问文件服务器,开启IPv6,让你的局域网可以使用IPV6进行共享文件夹的访问...
- 【Java】23 函数式编程
- Hazel游戏引擎(005)入口点
- 1GB等于多少MB?
- 燕山大学计算机历年拟录取分数线,燕山大学录取分数线2021是多少分(附历年录取分数线)...
- 计算机收藏夹位于哪个磁盘,win10收藏夹在电脑什么位置_win10系统收藏夹在哪里...
- 检修计算机硬件故障的流程,计算机硬件日常管理维护及故障检修
- 搬家了,新的网站地址
- ROS学习笔记74(TF Using Stamped datatypes with tf::MessageFilter)
热门文章
- java ftpclient quit_一步一步android(6):关于FtpClient类的学习
- Docker:设置容器自动启动
- 实战CSS:苏宁商城静态实现
- Python:eval函数
- java服务端验证框架_SpringBoot服务端数据校验过程详解
- 《编写高质量代码:改善Java程序的151条建议》读书笔记
- 2020年GitHub上的7个顶级的Java开源目推荐(强烈安利)
- 数学_最小二乘问题的求解
- 论文笔记_S2D.21_2014-CVPR_单张图像的离散-连续深度估计
- Ehcache 3.7文档—基础篇—XML Configuration