什么是APT?
APT(Annotation Processing Tool)注解处理工具,它是jdk提供的一套工具,通过这套工具我们可以在编译时,根据注解自动生成Java代码。
注解
注解可以理解为代码的标识,不会对运行有直接影响。
1、内置注解
Java内置几个常用注解,这部分标识源码,会被编译器识别,提示错误等。
@Override 标记覆盖方法
@Deprecated 标记为过时
@SuppressWarnings 忽略警告
2、元注解
假设我们要自定义一个注解,这时候就需要用元注解去声明自定义注解,包括像注解作用域、生命周期等。
@Documented
可以被javadoc文档化。
@Target
注解用在哪
CONSTRUCTOR
构造函数
FIELD
域声明
LOCAL_VARIABLE
局部变量
METHOD
方法
PACKAGE
包声明
PARAMETER
参数
TYPE
类、接口
@Inherited
允许子类继承父类注解
@Retention
SOURCE
:编译时剔除
CLASS
:在CLASS文件保留标识,运行时剔除
RUNTIME
运行时保留标识
3、自定义注解
使用元注解创建自定义注解
//直到运行时还保留注解
@Retention(RetentionPolicy.RUNTIME)
//作用域 类
@Target(ElementType.TYPE)
public @interface BindV {
int resId() default 0;
}
4、解析注解
在代码中定义了标识,现在想拿到这些信息,可以通过反射、APT获取注解的值。
1、反射
通过反射解析注解(这里也不阐述什么是反射)
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ReadAnnotation {
String value() default "read annotation defalult";
}
//MainActivity.java 文件
@ReadAnnotation("test")
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Class clz = MainActivity.class;
ReadAnnotation read = (ReadAnnotation) clz.getAnnotation(ReadAnnotation.class);//反射拿到类的注解
TextView textview=findViewById(R.id.txt);
textview.setText(read.value());
}
}
2、APT
APT(Annotation Processing Tool)注解处理器,是在编译期间读取注解,生成Java文件。反射解析注解是损耗性能的,接下来通过APT来生成java源码,从而避免反射。
1. 在之前的项目lib_annotation上创建新的注解AptTest
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface AptTest {
String value() default "atp default String";
}
2. 创建注解处理(java)项目 lib_compiler
原理为通过获取注解参数,通过拼接字符串方式生成代码,然后写入文件。通过javapeat可以减少导包等操作。
@SupportedAnnotationTypes({"com.yy.lib_annotation.AptTest"})//声明需要匹配的注解
@SupportedSourceVersion(SourceVersion.RELEASE_8)//支持的Java版本
public class AptProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Set<? extends Element> element = roundEnvironment.getElementsAnnotatedWith(AptTest.class);
StringBuilder sb=new StringBuilder("all aptTest values is:");
for (Element ele:element) {
TypeElement typeelement = (TypeElement) ele;
AptTest aptTest=typeelement.getAnnotation(AptTest.class);
sb.append(aptTest.value());
sb.append("|");
}
//新建一个方法
MethodSpec getString = MethodSpec.methodBuilder("getString")
.addModifiers(Modifier.PUBLIC)
.returns(String.class)
.addCode("return \""+sb.toString()+"\";\n")
.build();
//新建一个类
TypeSpec testType = TypeSpec.classBuilder("APTTest").addModifiers(Modifier.PUBLIC).addMethod(getString).build();
//生成这个类
JavaFile javaFile = JavaFile.builder("com.yy.aptdemo", testType)
.build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
System.out.println(e);
}
return true;
}
}
项目结构变为
项目结构
3. lib_compiler注册处理器
gradle4可以直接用auto-service注解进行自动注册,现在新建的是gradle版本是5以上的,所以要手动注册,手动注册方法如下:
创建一个
META-INF/services/javax.annotation.processing.Processor文件,
其内容是一系列的自定义注解处理器完整有效类名集合,以换行切割:
com.yy.lib_compiler.AptProcessor
4.引入运行项目
在app项目中,
在build.gradle文件中引入
dependencies {
implementation project(path: ':lib_annotation')
annotationProcessor project(path: ':lib_compiler')
}
修改MainActivity
@ReadAnnotation("test")
@AptTest("this is apt from annotation")
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Class clz = MainActivity.class;
ReadAnnotation read = (ReadAnnotation) clz.getAnnotation(ReadAnnotation.class);//拿到布局文件id
TextView textview=findViewById(R.id.txt);
textview.setText(read.value());
//
textview.setOnClickListener(view -> Toast.makeText(MainActivity.this,new APTTest().getString(),Toast.LENGTH_SHORT).show());
}
}
运行,点击就可以得到AptTest的内容,同时我们可以在看到生成的类
apt生成的类文件
外记:有时我们生成的文件不同项目传入不同参数,可以在项目的build.gradle文件中配置能数
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
javaCompileOptions.annotationProcessorOptions.arguments=[prjKey:project.getName()]//传入参数
}
在处理器中类增加注解
@SupportedOptions("prjKey")//指定可以接收从build.gradle中传过来的参数prjKey
就可以得到传入的参数
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
Map<String, String> opts = processingEnvironment.getOptions();
prjKey=opts.get("prjKey");
}
本文示例代码已传至github
网友评论