注解处理器(Annotation Processor)是javac内置的一个用于编译时扫描和处理注解(Annotation)的工具。简单的说,在源代码编译阶段,通过注解处理器,我们可以获取源文件内注解(Annotation)相关内容。
由于注解处理器可以在程序编译阶段工作,所以我们可以在编译期间通过注解处理器进行我们需要的操作。比较常用的用法就是在编译期间获取相关注解数据,然后动态生成.java源文件(让机器帮我们写代码),通常是自动产生一些有规律性的重复代码,解决了手工编写重复代码的问题,大大提升编码效率。
butterknife,Dagger2,EventBus…
用来标注其它注解而创建的新注解,元注解的类型有以下几种:
其中@Target注解的取值是一个ElementType类型的枚举,其中有一下几种取值,对应不同的对象范围。
其中@Retention注解有3种类型,分别表示不同级别的保留策略。
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface BindView {
int value() default -1;
}
@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {
private Filer mFiler;
private Messager mMessager;
private Elements mElementUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnvironment.getFiler();
mMessager = processingEnvironment.getMessager();
mElementUtils = processingEnvironment.getElementUtils();
}
@Override
public boolean process(Set set, RoundEnvironment roundEnvironment) {
Set bindViewElements = roundEnvironment.getElementsAnnotatedWith
(BindView.class);
for (Element element : bindViewElements) {
//1.获取包名
PackageElement packageElement = mElementUtils.getPackageOf(element);
String packgeName = packageElement.getQualifiedName().toString();
note(String.format("package = %s", packgeName));
//2.获取包装类类型
TypeElement typeElement = (TypeElement) element.getEnclosingElement();
String ecclosingName = typeElement.getQualifiedName().toString();
note(String.format("enclosindClass = %s", ecclosingName));
//因为BindView只作用于filed,所以这里可直接进行强转
VariableElement bindViewElement = (VariableElement) element;
//3.获取注解的成员变量名
String bindViewFiledName = bindViewElement.getSimpleName().toString();
//3.获取注解的成员变量类型
String bindViewFiledClassType = bindViewElement.asType().toString();
//4.获取注解元数据
BindView bindView = element.getAnnotation(BindView.class);
int id = bindView.value();
note(String.format("%s %s = %d", bindViewFiledClassType, bindViewFiledName, id));
//4.生成文件
createFile(typeElement, bindViewFiledClassType, bindViewFiledName, id);
}
return false;
}
@Override
public Set getSupportedAnnotationTypes() {
Set annotations = new LinkedHashSet<>();
annotations.add(BindView.class.getCanonicalName());
return annotations;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
private void note(String msg) {
mMessager.printMessage(Diagnostic.Kind.NOTE, msg);
}
private void createFile(TypeElement enclosingElement, String bindViewFiledClassType, String bindViewFiledName, int id) {
String pkName = mElementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
try {
JavaFileObject jfo = mFiler.createSourceFile(pkName + ".ViewBinding", new Element[]{});
Writer writer = jfo.openWriter();
writer.write(brewCode(pkName, bindViewFiledClassType, bindViewFiledName, id));
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private String brewCode(String pkName, String bindViewFiledClassType, String bindViewFiledName, int id) {
StringBuilder builder = new StringBuilder();
builder.append("package " + pkName + ";\n\n");
builder.append("//Auto generated by apt,do not modify!!\n\n");
builder.append("public class ViewBinding { \n\n");
builder.append("public static void main(String[] args){ \n");
String info = String.format("%s %s = %d", bindViewFiledClassType, bindViewFiledName, id);
builder.append("System.out.println(\"" + info + "\");\n");
builder.append("}\n");
builder.append("}");
return builder.toString();
}
}
@AutoService(Processor.class) :向javac注册我们这个自定义的注解处理器,这样,在javac编译时,才会调用到我们这个自定义的注解处理器方法。
牢记Annotation Process的实质用处就是在编译时通过注解获取相关数据,
那么,在这个Demo里面,我们就直接在编译时打印出我们注解的数据的成员变量名,成员变量类,包装类类名,包名和注解元数据进行显示,然后将这些信息写入到一个.java文件中,这里我就简单的直接输出这些信息进行显示。
按照上面自定义注解处理的方法,我们操作如下:
apply plugin: 'com.android.application'
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath = true
}
}
}
}
dependencies {
...
implementation project(':AnnotationLib')
}
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv)
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
编译时在console中输出:
最后,我们根据注解获取到的数据还生成了一个java文件,其生成路径:app\build\generated\source\apt\debug\com\hiten\test\ViewBinding.java
具体内容如下:
在app的build中 android {
...
defaultConfig {
...
//添加如下配置就OK了
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath = true
}
}
}
注解处理器(Annotation Processor)简析
编译时注解处理器AnnotationProcessor的使用