使用注解标记元数据,可用于在工程编译期间,解析注解标记的元素,自动生成java文件和代码。
常用的三个工具
annotationProcessor
annotationProcessor和android-apt的功能是一样的,它们是替代关系。是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。
AutoService
主要的作用是注解 processor 类,并对其生成 META-INF 的配置信息。
META-INF 的配置信息作用:相当于一个信息包,目录中的文件和目录获得Java 2平台的认可与解释,用来配置应用程序、扩展程序、类加载器和服务
JavaPoet
这个库的主要作用就是帮助我们通过类调用的形式来生成代码。
实践
目的:
通过注解Activity中的字段,生成初始化代码,类似butterKnife的@bindView注解的作用
第一步:声明注解
在项目中新建一个叫做annotationlib
的Java library,用来定义我们在项目中需要使用到的注解,这个lib下面的代码会直接打包到我们项目中
DIActivity:用来修饰类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface DIActivity {
}
DIView:用来修饰字段
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DIView {
int value() default 0;
}
第二步:解析注解,生成代码
再次新建一个Java Library,命名为compilerlib
,这个库的主要作用:
- 项目在编译期间,扫描项目中用到的注解,然后解析注解,生成Java代码
- 该项目会以
annotationProcessor
的方式依赖到项目中,不会参与实际的打包
- build.gradle文件添加需要用到第三方库
//主要的作用是注解 processor 类,并对其生成 META-INF 的配置信息。
implementation 'com.google.auto.service:auto-service:1.0-rc3'
//主要作用就是帮助我们通过类调用的形式来生成代码。
implementation 'com.squareup:javapoet:1.10.0'
api project(':annotationlib')
- DIProcessor 解析注解,生成Java文件以及代码
@AutoService(Processor.class)
public class DIProcessor extends AbstractProcessor {
private Elements elementUtils;
@Override
public Set getSupportedAnnotationTypes() {
// 规定需要处理的注解
return Collections.singleton(DIActivity.class.getCanonicalName());
}
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
System.out.println("DIProcessor");
Set extends Element> elements = roundEnv.getElementsAnnotatedWith(DIActivity.class);
for (Element element : elements) {
// 判断是否Class
TypeElement typeElement = (TypeElement) element;
List extends Element> members = elementUtils.getAllMembers(typeElement);
MethodSpec.Builder bindViewMethodSpecBuilder = MethodSpec.methodBuilder("bindView")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(TypeName.VOID)
.addParameter(ClassName.get(typeElement.asType()), "activity");
for (Element item : members) {
DIView diView = item.getAnnotation(DIView.class);
if (diView == null){
continue;
}
bindViewMethodSpecBuilder.addStatement(String.format("activity.%s = (%s) activity.findViewById(%s)",item.getSimpleName(),ClassName.get(item.asType()).toString(),diView.value()));
}
TypeSpec typeSpec = TypeSpec.classBuilder("DI" + element.getSimpleName())
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(bindViewMethodSpecBuilder.build())
.build();
JavaFile javaFile = JavaFile.builder(getPackageName(typeElement), typeSpec).build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
private String getPackageName(TypeElement type) {
return elementUtils.getPackageOf(type).getQualifiedName().toString();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elementUtils = processingEnv.getElementUtils();
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_7;
}
}
第三步:使用注解
在MainActivity中使用注解:@DIActivity , @DIView()
@DIActivity
public class MainActivity extends AppCompatActivity {
@DIView(R.id.text1)
TextView textView;
@DIView(R.id.text2)
TextView textView2;
@DIView(R.id.text3)
TextView textView3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//由compilerlib 生成的
DIMainActivity.bindView(this);
textView.setText("Hello Worldaaaaaaaaaaaa");
}
}
DIMainActivity是由compilerlib在编译期间生成,代码如下:
public final class DIMainActivity {
public static void bindView(MainActivity activity) {
activity.textView = (android.widget.TextView) activity.findViewById(2131165316);
activity.textView2 = (android.widget.TextView) activity.findViewById(2131165317);
activity.textView3 = (android.widget.TextView) activity.findViewById(2131165318);
}
}