其他分享
首页 > 其他分享> > 阿里大神手把手教你Android ButterKnife 的实现思路,建议收藏!

阿里大神手把手教你Android ButterKnife 的实现思路,建议收藏!

作者:互联网

在Android开发中,我们为了方便初始化Activity中的各种View,我们可能会使用到Jake Wharton的 ButterKnife库,这个库是针对View、资源id等进行注解的开源库,它能够去除掉一些丑陋不堪的样板式代码,使得我们的代码更加简洁、易于维护,同时基于APT也使得它的效率得到保证。 (如果你想快速了解ButterKnife的实现思路,可以先阅读 ExampleActivity$InjectAdapter类以及后续的结论,然后再回过头来阅读 )

阿里大神手把手教你Android ButterKnife 的实现思路,建议收藏!

阿里大神手把手教你Android ButterKnife 的实现思路,建议收藏!

当运行完onCreate函数之后Activity中的几个View就已经被初始化了。findViewById、强制转换等样板代码被去除了,代码变得更加简单,使得我们可以更专注在代码逻辑的编写上,整个类型也更易于维护。

那么ButterKnife的原理是什么呢?@InjectView又是什么?ButterKnife的inject函数又有什么作用?

说了这么多还是太抽象了,还是以小民的例子来为大家一一解除疑问吧。

小民自从知道ButterKnife之后也被它的魅力所吸引了,于是决定研究个究竟,经过一番搜索得知ButterKnife是基于编译时注解,然后通过APT生成辅助类,然后在运行时通过inject函数调用那些生成的辅助类来完成功能。小民决定自己写一个只支持View 的id注入的简版ButterKnife来深入学习,这个库被命名为SimpleDagger。

首先小民建了一个注解类,代码如下 :

阿里大神手把手教你Android ButterKnife 的实现思路,建议收藏!

因为我们的这个注解只支持View的id注入,因此它的目标元素是字段,它只存在在class文件中,因为一旦过了编译期我们就不再需要它了。关于注解方面的基础知识我们不做过多讲解,对这方面不了解的同学可以先阅读相关书籍,例如《Java编程思想》、《Java核心技术》。

在添加AbstractProcessor 之前,为了使Eclipse支持 APT 需要一些配置,可以参考 injectdagger。Android Studio要支持 APT则需要添加APT插件,有兴趣的同学可以自行搜索相关解决方案。

通过 APT 来生成辅助类型

添加这个注解之后,我们还需要在编译期对这个注解进行处理。上文说到,编译器会在编译时检测所有的AbstractProcessor并且调用它的process函数来让开发人员对代码元素进行处理。因此我们新建一个AbstractProcessor的子类,代码如下 :

阿里大神手把手教你Android ButterKnife 的实现思路,建议收藏!

在ViewInjectorProcessor类的上面我们看到如下注解@SupportedAnnotationTypes(“org.simple.injector.anno.*”), 这个注解表明这个类只支持org.simple.injector.anno路径下的注解,我们的ViewInjector注解就是在这个包下。在该类的init函数中我们注册了一个注解处理器,也就是ViewInjectHandler类,该类实现了AnnotationHandler接口,该接口的声明如下 :

阿里大神手把手教你Android ButterKnife 的实现思路,建议收藏!

该接口声明了两个函数,一个是关联ProcessingEnvironment,另一个是handleAnnotation函数,负责处理标识了ViewInjector注解的元素。小民的设计思路是定义一个AnnotationHandler接口,每个实现类处理一种类型的注解,例如ViewInjectHandler只处理ViewInject注解。下面我们看看ViewInjectHandler的核心代码 :

在handleAnnotation函数中小民获取了所有被ViewInject注解标识了的VariableElement元素,然后将这些元素按照宿主类进行分类存到一个map中,key就是宿主类的完整类路径,value就是这个宿主类中的所有被标识了ViewInject的VariableElement元素列表。

例如将上述ExampleActivity的示例替换成小民的SimpleDagger,使用ViewInject注解标识中三个View,代码如下 :

阿里大神手把手教你Android ButterKnife 的实现思路,建议收藏!

阿里大神手把手教你Android ButterKnife 的实现思路,建议收藏!

阿里大神手把手教你Android ButterKnife 的实现思路,建议收藏!

在AbsWriter的generate函数中,我们定义了一个生成辅助类的逻辑骨架,分别为获取宿主类型的所有元素,并且通过第一个元素获取宿主类所在的包以及构建辅助类的类名等,然后创建一个新的java类,最后分别写入import、所有被注解的元素等信息写入到辅助类当中,所有生成的辅助类都是InjectAdapter的子类。实现代码如下的功能在DefaultJavaFileWriter类中,核心代码如下 :

阿里大神手把手教你Android ButterKnife 的实现思路,建议收藏!

在DefaultJavaFileWriter中分别写入了辅助类的各个部分,最终的是写入字段的部分,也就是writeField函数。在该函数中,小民获取了这个字段的名字,并且写下了一行如下一行代码

target.fieldName = ViewFinder.findViewBydId(target, ViewInject注解的值);

InjectAdapter 接口

InjectAdapter的声明如下 :

阿里大神手把手教你Android ButterKnife 的实现思路,建议收藏!

ExampleActivity$InjectAdapter 类

这相当于我们为每个元素都生成一行初始化代码来替换手动在ExampleActiivty中进行findViewById,当我们在ExampleAcivity的onCreate函数中调用SimpleDagger的inject函数时,会将ExampleActivity传递到InjectAdapter中,因此最后为ExampleActivity生成的辅助类就成为了如下这样 :

阿里大神手把手教你Android ButterKnife 的实现思路,建议收藏!

当调用SimpleDagger的inject时就会先通过传递进来的类名构建一个InjectAdapter子类的类名,例如传递进来的是ExampleActivity,那么此时的辅助类的类名为 ExampleActivity$InjectAdapter,它InjectAdapter的子类。

拿到完整类名之后再反射构建一个对象,然后转换为InjectAdapter,最后调用inject函数。而这个生成的ExampleActivity$InjectAdapter的inject函数中又对每个View进行了findViewBydId,也就是对它们进行了初始化。至此,这些View字段就被自动初始化了!

我们最后再来捋一捋这个过程,大致分为如下几步 :

  1. 通过ViewInject注解标识一些View成员变量;
  2. 通过ViewInjecyProcessor捕获添加了ViewInject注解的元素,并且按照宿主类进行分类;
  3. 为每个含有ViewInject注解的宿主类生成一个InjectAdapter辅助类,并且在它的inject函数中生成初始化View的代码;
  4. 在SimpleDagger的inject函数中构建生成的辅助类,此时内部会它这个InjectAdapter辅助类的inject函数,这个函数中又会初始化宿主类中的View成员变量,至此,View就已经被初始化了。

SimpleDagger的完整代码在这里,有兴趣的同学可以下载下来进行学习以及扩展。

需要注意的是在eclipse中使用APT需要添加JRE库的引用,在Android Studio则需要引用APT的插件。

会持续更新哦,文章不易希望大家多多关注评论收藏!!!谢谢。

标签:函数,手把手,代码,InjectAdapter,ButterKnife,注解,Android,ExampleActivity,View
来源: https://blog.51cto.com/14775360/2483206