ButterKnife在实际开发中有着大量运用,其强大的view绑定和click事件处理,使得开发效率大大提高,同时增加了代码的阅读性又不影响其执行效率
注解的分类
注解主要有两种分类,一个是运行时,一个是编译时
运行时注解:由于会影响性能,不是很推荐使用
编译时注解:编译时注解的核心原理依赖APT(Annotation Processing Tools)
实现
编译时注解说明
使用了编译时注解的第三方框架主要有
ButterKnife
:这个库是针对View,资源ID,部分事件等进行注解的开源库,它能够去除掉一些不怎么雅观的样板式代码,使得我们的代码更加简洁,易于维护,同事给予APT也能使得它的效率得到保证。ButterKnife
是针对View
等进行注解的开源库Dragger
Retrofit
核心原理:APT(Annotation Processing Tools)实现
编译时Annotation
解析的基本原理是,在某些代码元素上(如类型、函数、字段等)添加注解,在编译时javac编译器会检查AbstractProcessor
的子类,并且调用该类型的process
函数,然后将添加了注解的所有元素都传递到process
函数中,使得开发人员可以在编译器进行相应的处理,例如,根据注解生成新的Java类,这也就是ButterKnife
Dragger
等开源库的基本原理
APT工具APT(Annotation Processing Tool)
是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation
,使用Annotation
进行额外的处理。Annotation
处理器在处理Annotation
时可以根据源文件中的Annotation
生成额外的源文件和其它的文件(文件具体内容由Annotation
处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件
Java源文件编译成Class文件
工具是javac工具,注解处理器是一个在javac中的,用来编译时扫描和处理的注解的工具。你可以为特定的注解,注册你自己的注解处理器
怎样注册注解处理器MyProcessor
到javac中。你必须提供一个.jar
文件。就像其他.jar文件一样,你打包你的注解处理器到此文件中。并且,在你的jar中,你需要打包一个特定的文件javax.annotation.processing.Processor
到META-INF/services
路径下
依赖准备
需要一个注入模块,也就是一个依赖库,这是一个Android Library
新建Android依赖库项目右键 -> New -> Module -> Android Library
需要一个Java Library
,用来包含注解,注解库
新建Java依赖库项目右键 -> New -> Module -> Java Library
同时就需要一个注解处理器,这也是一个Java Library
,编译库
新建Java依赖库项目右键 -> New -> Module -> Java Library
至此,主项目,Android依赖库,Java依赖库新建完成
添加依赖
工程gradle增加maven库和apt插件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27buildscript {
repositories {
google()
jcenter()
//maven库
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.3'
//apt插件,如果报错则不使用apt,不添加这行
//classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
allprojects {
repositories {
google()
jcenter()
//maven库
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
主工程gradle下引入apt1
2
3
4
5
6
7apply plugin: 'com.android.application'
//引入apt,如果报错则不使用apt,不添加这行
//apply plugin: 'com.neenbedankt.android-apt'
android {
compileSdkVersion 25
···
配置主项目,Android依赖和Java依赖之间的关系项目右键 -> Open Module Settings -> ···
如果配置完成报错,那么就不添加apt
,而使用annotationProcessor
1
android-apt plugin is incompatible with the Android Gradle plugin. Please use 'annotationProcessor' configuration instead.
此时再主项目gradle中会生成1
2
3
4
5dependencies {
···
implementation project(':inject')
implementation project(':inject-complier')
}
将其修改为,apt
和annotationProcessor
两种形式1
2
3
4
5
6dependencies {
···
implementation project(':inject')
//apt project(':inject-complier')
annotationProcessor project(':inject-complier')
}
否则编译器不知道由谁去负责apt
添加inject-complier
的依赖inject-annotation
同样的做法,inject
添加inject-annotation
依赖
在inject-complie
添加生成源代码的com.google.auto:auto-common`
com.google.auto.service:auto-service库和生成Java源文件的
com.squareup:javapoet`库
配置完成gradle如下1
2
3
4
5
6
7dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':inject-annotation')
implementation 'com.google.auto:auto-common:0.10'
implementation 'com.google.auto.service:auto-service:1.0-rc4'
implementation 'com.squareup:javapoet:1.11.1'
}
最终生成的各个gradle依赖如下
主项目gradle1
2
3
4
5
6
7dependencies {
···
implementation project(':inject')
//apt project(':inject-complier')
annotationProcessor project(':inject-complier')
implementation project(':inject-annotation')
}
Android Library1
2
3
4dependencies {
···
implementation project(':inject-annotation')
}
注解库依赖没有添加任何依赖
编译库依赖1
2
3
4
5
6
7dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':inject-annotation')
implementation 'com.google.auto:auto-common:0.10'
implementation 'com.google.auto.service:auto-service:1.0-rc4'
implementation 'com.squareup:javapoet:1.11.1'
}
代码实现
主项目代码1
2
3
4
5
6
7
8
9
10
11
12
13
14public class MainActivity extends AppCompatActivity {
(R.id.text_view)
TextView textView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InjectView.bind(this);
Toast.makeText(this, "textView=" + textView, Toast.LENGTH_SHORT).show();
textView.setText("改写成功");
}
}
Android依赖库代码1
2
3public interface ViewBinder<T> {
void bind(T tartget);
}
1 | public class InjectView { |
注解库代码1
2
3
4
5 (RetentionPolicy.CLASS)
(ElementType.FIELD)
public BindView {
int value();
}
编译库代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class FieldViewBinding {
private String name;
private TypeMirror typeMirror;
private int resId;
public FieldViewBinding(String name, TypeMirror typeMirror, int resId) {
this.name = name;
this.typeMirror = typeMirror;
this.resId = resId;
}
public String getName() {
return name;
}
public TypeMirror getTypeMirror() {
return typeMirror;
}
public int getResId() {
return resId;
}
}
1 | (Processor.class) |
由于编译库内不能存在中文,故将代码注释单独解释
@AutoService
这是一个其他注解处理器中引入的注解。AutoService
注解处理器是Google开发的,用来生成META-INF/services/javax.annotation.processing.Processor
文件的。我们可以在注解处理器中使用注解。非常方便Elements
一个用来处理Element的工具类:在注解处理过程中,我们扫描所有的Java源文件。源代码的每一个部分都是一个特定类型的Element
。换句话说:Element
代表程序的元素,例如包、类或者方法。每个Element
代表一个静态的、语言级别的构件PackageElement
:包元素,可以获取包名TypeElement
:类型元素,某个字段属于某种类型ExcutableElement
:可执行元素VariabeElement
:变量元素TypeParameterElement
:类型参数元素Types
一个用来处理TypeMirror
的工具类Filer
使用Filer
你可以创建Java文件process(Set<? extends TypeElement> annotations, RoundEnvironment env)
相当于每个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。输入参数RoundEnviroment
,可以让你查询出包含特定注解的被注解元素getSupportedAnnotationTypes
这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。换句话说,你在这里定义你的注解处理器注册到哪些注解上getSupportedSourceVersion
用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported()
。如果你有足够的理由只支持Java 6
的话,你也可以返回SourceVersion.RELEASE_6