在《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添加自定义组件原理相关推荐

  1. SpingMVC框架:fileUpload组件原理和实现

    QUESTION:fileUpload组件原理和实现 ANSWER: 目录 QUESTION:fileUpload组件原理和实现 ANSWER: 一:异常产生 查询了一系列博客后,发现这是由于上传文件 ...

  2. SpringCloud_004_SpringCloud服务发现组件原理介绍

    SpringCloud_004_SpringCloud服务发现组件原理介绍 技术交流qq群,交流起来方便一些:170933152 1.如何解决硬编码问题? 上次咱们说到,硬编码问题 比如: 服务发现组 ...

  3. QGC添加自定义组件和发送自定义MAVLINK消息

    QGC添加自定义组件和发送自定义MAVLINK消息 一.添加自定义组件 1.1 在飞行界面添加组件 1.2 实现组件事件 1.3 在MOCK模拟链接中实现验证 1.4 验证 二.自定义MAVLINK消 ...

  4. Flutter 动画全解析(动画四要素、动画组件、隐式动画组件原理等)

    本文通过拆解 Flutter 中动画的实现方式以及原理来介绍动画实现的整个过程. 1. 动画四要素 动画在各个平台的实现原理都基本相同,是在一段时间内一系列连续变化画面的帧构成的.在 Flutter ...

  5. springboot继承组件_SpringBoot如何扩展引入的组件,以及如何自动配置组件原理

    大家都知道,当我们创建SpringBoot项目之后,我们可以在pom文件下,引入我们想要启动的组件,当我们引入之后,SpringBoot会自动帮我们配置属性! 下面我们以SpringBoot引入Spr ...

  6. Spring Cloud一些组件原理(一)

    Spring Cloud的一般组件: Eureka:注册中心,这个必须要放在第一个说,毕竟没有注册中心就没有服务的自动伸缩,就谈不上微服务了: Ribbon.Feign:远程调用,没有远程调用也还是谈 ...

  7. Universal-Image-Loader,android-Volley,Picasso、Fresco和Glide五大Android开源组件加载网络图片的优缺点比较

    在android中的加载网络图片是一件十分令人头疼的事情,在网上有着许多关于加载网络图片的开源库,可以让我们十分方便的加载网络图片.在这里我主要介绍一下我自己在使用Volley, Picasso, U ...

  8. Kubernetes——K8s架构与组件原理

    摘要 通过现代的 Web 服务,用户希望应用程序能够 24/7 全天候使用,开发人员希望每天可以多次发布部署新版本的应用程序. 容器化可以帮助软件包达成这些目标,使应用程序能够以简单快速的方式发布和更 ...

  9. Netty异步非阻塞事件驱动及组件原理详解

    本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件.整体架构,知其然且知其所以然,希望给大家在实际开发实践.学习开源项目方面提供参考. Netty 是一个异步事件驱动的网络应用程序 ...

  10. 你真的完全掌握了 Input 组件的键盘控制么? ——百度智能小程序 Input 组件原理剖析与键盘行为说明

    在百度智能小程序的很多开发场景中,我们都会使用到 Input 输入框组件.而在操作输入框的过程中,正确处理键盘的弹出和收起行为也是十分重要的一环.键盘行为不仅需要完美符合业务场景,同时也和用户体验息息 ...

最新文章

  1. 现代软件工程 第十章 【典型用户和场景】 练习与讨论
  2. Linux运维工程师可是很吃香的
  3. CentOS下如何完全卸载MySQL?解决卸载不干净的问题
  4. WebService服务发布与使用(JDK自带WebService)
  5. Laravel 深入核心系列教程
  6. 防碰撞算法 matlab仿真,基于毫米波雷达的汽车防撞系统的设计
  7. php opendir(),php之opendir()函数的用法
  8. jeecgboot修改登录界面、背景图等的页面记录
  9. 最新版PandoraBox潘多拉安装adbyby去广告插件图文详细教程!!
  10. 较新颖的智能优化算法
  11. 矩阵的分解——LU分解
  12. ipv6访问文件服务器,开启IPv6,让你的局域网可以使用IPV6进行共享文件夹的访问...
  13. 【Java】23 函数式编程
  14. Hazel游戏引擎(005)入口点
  15. 1GB等于多少MB?
  16. 燕山大学计算机历年拟录取分数线,燕山大学录取分数线2021是多少分(附历年录取分数线)...
  17. 计算机收藏夹位于哪个磁盘,win10收藏夹在电脑什么位置_win10系统收藏夹在哪里...
  18. 检修计算机硬件故障的流程,计算机硬件日常管理维护及故障检修
  19. 搬家了,新的网站地址
  20. ROS学习笔记74(TF Using Stamped datatypes with tf::MessageFilter)

热门文章

  1. java ftpclient quit_一步一步android(6):关于FtpClient类的学习
  2. Docker:设置容器自动启动
  3. 实战CSS:苏宁商城静态实现
  4. Python:eval函数
  5. java服务端验证框架_SpringBoot服务端数据校验过程详解
  6. 《编写高质量代码:改善Java程序的151条建议》读书笔记
  7. 2020年GitHub上的7个顶级的Java开源目推荐(强烈安利)
  8. 数学_最小二乘问题的求解
  9. 论文笔记_S2D.21_2014-CVPR_单张图像的离散-连续深度估计
  10. Ehcache 3.7文档—基础篇—XML Configuration