ButterKnife(https://github.com/JakeWharton/butterknife)是一款android平台的依赖注入框架,通过该工具可以实现View、OnClickListener的注入,省去了findViewById、setOnClickListener的过程。使用方法如下:
![](https://img.haomeiwen.com/i5328002/958afb493caa6620.png)
通过@BindView注解实现findViewById的功能,完成View注入;通过@OnClick完成OnClickListener点击事件的注入,给ID对应的View设置点击事件和响应函数。关于注解的定义和解析可以参照这篇文章:Java注解。ButterKnife使用的就是编译时解析注解的技术,在编译时对注解进行解析,生成中间文件,在ButterKnife.bind时引用注解编译器生成的中间文件,完成依赖注入。
注解的定义
![](https://img.haomeiwen.com/i5328002/348dd576219d3a44.png)
BindView注解定义中使用了元注解@Retention(CLASS)定义了该注解只保留到编译期间,运行时会丢弃;@Target(FIELD)表示该注解只能用在成员变量上面。
![](https://img.haomeiwen.com/i5328002/05cb5b1390314ee3.png)
OnClick注解中@Target(METHOD)表示该注解只能用于方法上;
![](https://img.haomeiwen.com/i5328002/5ec441d686e9530d.png)
ListenerClass是一个@Target(ANNOTATION_TYPE)类型的注解,表示ListenerClass只能用在注解上;且@Retention(RUNTIME)表示该注解可以保留到JVM中,也就是运行时能够通过反射来获取。
注解的解析
下面对@BindView和@OnClick两种注解的解析进行讲解。编译时注解的解析:
编译时 Annotation 指 @Retention 为 CLASS 的 Annotation,由编译器自动解析。需要做的
a. 自定义类集成自 AbstractProcessor(编译器在编译时自动查找所有继承自 AbstractProcessor 的类,然后调用他们的 process 方法去处理)
b. 重写其中的 process 函数
ButterKnife实现了ButterKnifeProcessor来进行编译时注解的解析:
![](https://img.haomeiwen.com/i5328002/7b2f489b827e7ef5.png)
ButterKnifeProcessor.process()函数如下:
![](https://img.haomeiwen.com/i5328002/6d72c257641ce828.png)
process函数先调用findAndParseTargets生成bindingMap,然后通过binding.brewJava老生成Java文件。findAndParseTargets的实现如下(这里只关注@BindView和@OnClick):
![](https://img.haomeiwen.com/i5328002/69fa614836adb85b.png)
其中调用parseBindView对注解为@BindView的Field进行解析;findAndParseListener对@OnClick之类的Listener注解进行解析。parseBindView代码如下:
![](https://img.haomeiwen.com/i5328002/5bc5e2acf71bf1f7.png)
parseBindView的主要工作是创建了BindingSet.Builder。getOrCreateBindingBuilder()如下:
![](https://img.haomeiwen.com/i5328002/a832a26d544b2754.png)
getOrCreateBindingBuilder内部调用了BindingSet.newBuilder。
![](https://img.haomeiwen.com/i5328002/3027f52f426d85be.png)
newBuilder生成了Builder对象,Builder对象定义了生成的Java文件名、mView所属对象的类型等。Builder对象生产后,parseBindView就根据@BindView注解信息生成FieldViewBinding对象,之后调用了Builder.build()函数;@BindView的解析已经完成后,最后通过BindingSet.brewJava来生成中间文件。@BindView在生成文件对应了如下:
![](https://img.haomeiwen.com/i5328002/2168f1f58a980b72.png)
生成的中间文件如下所示:
![](https://img.haomeiwen.com/i5328002/760301bd0fc0b6ce.png)
可以看到,中间文件里完成了对Target中成员变量的注入。
那么中间文件又是在什么时候被调用的呢?答案就是ButterKnife.bind(this)
![](https://img.haomeiwen.com/i5328002/ca8a8993183953b3.png)
bind函数根据调用的类名查找其对应的className_ViewBinding的类名,然后反射调用其构造函数。
至此,ButterKnife的@BindView的运行流程就是这样。
网友评论