APT的全称是AnnotationProcessingTool,它是一种注解处理器,在编译期间通过注解生成.java文件。既然是注解处理工具,那首先得有注解,注解的一般形式如下:
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
首先看到定义注解类需要关键字@Interface,然后在注解类的上面有两个注解
- @Retention:这个字段是定义该注解的生效时机,一共有三种类型SOURCE(只在源码阶段有效,也就是.java为转换成.class之前)、CLASS(编译期有效,就是说会被保留在.class文件中,但不会被虚拟机保留)、RUNTIME(会被保留到运行时,也就是说在运行时可以通过反射区获取注解信息)
- @Target:这个字段是声明注解所修饰的对象范围,一共有这几类TYPE(作用在类、接口,enum声明上),FIELD(作用在字段上(变量)),METHOD(作用在方法上),PARAMETER(作用在参数上),CONSTRUCTOR(作用在构造函数上),LOCAL_VARIABLE(作用在局部变量上),ANNOTATION_TYPE(作用在注解上),PACKAGE(作用在包上),TYPE_PARAMETER(用于类型参数声明1.8引入的)
其次在注解中定义了需要传递的参数名子为value,类型为int,注解支持的元素数据类型除了上述的int,还支持如下数据类型
- 所有基本类型(int,float,boolean,byte,double,char,long,short
- String
- Class
- enum
- Annotation
- 上述类型的数组
介绍完了注解之后,我们再看看APT 。我们直接从他的使用开始。
- 新建Android工程名字为APTTest
- 新建module(JavaLibrary)lib_annotation,这里主要存放注解,这里就一个类就是上面的BIndVIew
- 新建module(JavaLibrary)lib_processor,这里主要就是apt的内容。 在这里首先分两步,第一步新建一个类继承自AbstractProcessor,这里实现的processor其中的方法都已经在注释中
public class APTTestProcessor extends AbstractProcessor {
/**
* Elements提供了一些和元素相关的操作,如获取所在包的包名等
*/
private Elements mElementsUtil;
/**
* Filer用于文件操作,我们用它去创建生成的代码文件
*/
private Filer mFilerUtil;
/**
* 提供了和类型相关的操作,比如获取父类,两个类是不是父子关系
*/
private Types mTypesUtil;
/**
* 用于打印的,它会打印出Element所在的源代码,它还会抛出异常。靠默认的错误打印有时很难找出错误的地方,我们可以用它去添加更直观的日志打印
*/
private Messager mMessagerUtil;
/**
* 初始化,可以得到processingEnvironment,这个对象中包含了一些工具类如Elements,Types和Filer,Messager
*
* @param processingEnvironment
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
this.mElementsUtil = processingEnvironment.getElementUtils();
this.mFilerUtil = processingEnvironment.getFiler();
this.mMessagerUtil = processingEnvironment.getMessager();
this.mTypesUtil = processingEnvironment.getTypeUtils();
}
/**
* 具体的处理过程
*
* @param set:在getSupportedAnnotationTypes中声明的那些注解
* @param roundEnvironment:上下文环境
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
System.out.println("APTTestProcessor is working!");
// mMessagerUtil.printMessage(Diagnostic.Kind.ERROR,"ssssss");
return true;
}
/**
* 指定该处理器要处理的注解
*
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton(BindView.class.getCanonicalName());
}
/**
* 指定使用的Java版本,一般都是latestSupported
* 如果要指定某一具体的话直接return如 SourceVersion.RELEASE_8
*
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
// return SourceVersion.RELEASE_8;
return SourceVersion.latestSupported();
}
}
第二步新建文件路径为main/resources/META-INF/services/javax.annotation.processing.Processor,文件内容就一行代码,指定processor的全路径(注意上面加粗的内容一个字都不能错),com.mwy.test.lib_processor.APTTestProcessor
- 在APTTest中添加对这两个library的依赖,注意,注解处理器的依赖有点特殊,采用的是annotationProcessor
- 在lib_processor中添加lib_annotation的引用
这时候就可以使用了,我们build一下app可以看到如下信息

public interface Element extends AnnotatedConstruct {
/**
* @return:返回元素定义的类型
*/
TypeMirror asType();
/**
* @return:返回此元素的种类:包、类、接口、方法、字段等
*/
ElementKind getKind();
/**
* @return:返回此元素修饰符:public,private,static等
*/
Set<Modifier> getModifiers();
/**
* @return:返回元素的简单名称,如类名
*/
Name getSimpleName();
/**
* @return:返回包含当前元素的外层元素,后面会具体介绍
*/
Element getEnclosingElement();
/**
* @return:返回封装此元素的最里层元素,后面会具体介绍
*/
List<? extends Element> getEnclosedElements();
boolean equals(Object var1);
int hashCode();
/**
* @return:此元素中所有的注解
*/
List<? extends AnnotationMirror> getAnnotationMirrors();
/**
* @param var1
* @param <A>
* @return:返回此元素针对指定类型的注解
*/
<A extends Annotation> A getAnnotation(Class<A> var1);
<R, P> R accept(ElementVisitor<R, P> var1, P var2);
}
既然是一个接口,那势必会有他的具体实现类,这里直接的有5个:
- TypeElement:一个类或者接口程序元素,它的getEnclosingElement()返回的是当前类的包,getEnclosedElements()返回的是类或接口中直接声明的字段,方法,构造函数和成员类型。
- VariableElement:一个字段、enum常量,方法或构造方法参数、局部变量或者异常参数,它没有getEnclosedElement(),只有getEnclosingElement(),它返回的是TypeElement
- ExecutableElement:某个类或接口中的方法,构造方法或者初始化程序,他没有getEnclosingElement及getEnclosedElement方法
- PackageElement:一个包程序元素,它的getEnclosingElement返回的是null,getEnclosedElement返回的是当前包中顶级的class和interface
- TypeParameterElement:一个类,接口、方法或构造方法元素的泛型参数,它的getEnclosingElement()返回的是由此中类型参数参数化的泛型接口、类或者构造方法。
接下来我们就可以正式的开始了。直接上代码
public class APTTestProcessor extends AbstractProcessor {
/**
* Elements提供了一些和元素相关的操作,如获取所在包的包名等
*/
private Elements mElementsUtil;
/**
* Filer用于文件操作,我们用它去创建生成的代码文件
*/
private Filer mFilerUtil;
/**
* 提供了和类型相关的操作,比如获取父类,两个类是不是父子关系
*/
private Types mTypesUtil;
/**
* 用于打印的,它会打印出Element所在的源代码,它还会抛出异常。靠默认的错误打印有时很难找出错误的地方,我们可以用它去添加更直观的日志打印
*/
private Messager mMessagerUtil;
/**
* 初始化,可以得到processingEnvironment,这个对象中包含了一些工具类如Elements,Types和Filer,Messager
*
* @param processingEnvironment
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
this.mElementsUtil = processingEnvironment.getElementUtils();
this.mFilerUtil = processingEnvironment.getFiler();
this.mMessagerUtil = processingEnvironment.getMessager();
this.mTypesUtil = processingEnvironment.getTypeUtils();
}
/**
* 具体的处理过程
*
* @param set:在getSupportedAnnotationTypes中声明的那些注解
* @param roundEnvironment:上下文环境
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//因为注解是Variable上的,所以它的root就是TypeElement,所以packageName就为getEnclosingElement
//而当前rootElement就是class
for (Element rootElement : roundEnvironment.getRootElements()) {
String packageStr = rootElement.getEnclosingElement().toString();
String classStr = rootElement.getSimpleName().toString();
//新生成的className
ClassName className = ClassName.get(packageStr, classStr + "Binding");
//在类中生成新的方法public void bindView(){}
MethodSpec.Builder builder = MethodSpec.methodBuilder("bindView")
.addModifiers(Modifier.PUBLIC)
.returns(void.class)
.addParameter(ClassName.get(packageStr, classStr), "activity");
boolean hasAnnotation = false;
//既然是类,TypeElement,那么就遍历这个类,找到注解为BindView的Variable
for (Element enclosedElement : rootElement.getEnclosedElements()) {
if (enclosedElement.getKind() == ElementKind.FIELD) {
BindView bindView = enclosedElement.getAnnotation(BindView.class);
if (bindView != null) {
hasAnnotation = true;
builder.addStatement("activity.$N = activity.findViewById($L)", enclosedElement.getSimpleName(), bindView.value());
}
}
}
//创建一个class
TypeSpec builtClass = TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC)
.addMethod(builder.build())
.build();
if (hasAnnotation) {
try {
JavaFile.builder(packageStr, builtClass)
.build().writeTo(mFilerUtil);
} catch (IOException e) {
e.printStackTrace();
}
}
}
return true;
}
/**
* 指定该处理器要处理的注解
*
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton(BindView.class.getCanonicalName());
}
/**
* 指定使用的Java版本,一般都是latestSupported
* 如果要指定某一具体的话直接return如 SourceVersion.RELEASE_8
*
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
我觉得重要的都在注释中写了,在编译之后就可以看到生成了一个新的类

public class BindingApi {
public static void bindView(Activity activity) {
try {
Class clazz = Class.forName(activity.getClass().getCanonicalName() + "Binding");
Method method = clazz.getMethod("bindView", activity.getClass());
method.invoke(clazz.newInstance(), activity);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
这时候就可以完整的使用了,比如下面
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BindingApi.bindView(this);
textView.setText("this is autoBinding result");
}
}
只需在Activity中添加注解,以及在setContenVIew后调用bindVIew方法,就完成了view的初始化。