APT技术原理和简单实现
原理: 编写好的 Java 源文件,需要经过 javac 的编译,翻译为虚拟机能够加载解析的字节码 Class 文件。注解处理器是 javac 自带的一个工具,用来在编译时期扫描处理注解信息。你可以为某些注解注册自己的注解处理器。 注册的注解处理器由 javac调起,并将注解信息传递给注解处理器进行处理。
简单实现:
我们可以给我们的自定义注解写一个处理器来在编译的时候,处理一些逻辑,上面说到了原理就是在编译的时候如果我们注册了自己的注解处理器,javac就会帮我们调起,这样就能执行我们的逻辑了
第一步:
创建一个java的library来实现我们的注解处理器如图:
为什么要创建java的library而不是Android的,因为只有java的library才有AbstractProcessor这个类,我们实现自己的注解处理器需要继承这个AbstractProcessor,我们把这个library命名为compiler
第二步:再创建一个java的library放我们自定义的注解,为啥不创建Android的library,因为我们自定义的注解要在注解处理器中使用,所以也得创建一个自定义注解的java的library,我们把这个library命名为Annotation
我们再App的包下使用注解,引用关系 App引用compiler和Annotation,compiler引用Annotation
App:注意写法
compiler:
先把我们的注解定义出来:
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.FIELD})
public @interface MyViewBinding {
int value();
}
然后看一下注解的使用:
public class AnnotationTestActivity extends AppCompatActivity {
@MyViewBinding(R.id.tv_name)
public TextView textView;
@MyViewBinding(R.id.bt_jump)
public Button bt_jump;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_annotation);
}
}
下面就详细说一下注解处理器的实现:
1.首先创建一个类继承 AbstractProcessor,然后实现抽象方法process。
public class MyAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
return true;
}
}
这个process方法就是javac调用注解处理器的时候执行的方法,我们在这里可以处理我们自己的逻辑
2.然后需要表明我们这个注解处理器处理的是哪些注解:
有两种方式第一实现getSupportedAnnotationTypes方法:
public class MyAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Messager messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE, "注解处理器运行了");
return true;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> set = new TreeSet<>();
set.add("com.example.annotation.MyViewBinding");
return set;
}
}
可以看见返回值是一个Set,我们在set中加入我们的注解的全类名
运行结果:
第二用@SupportedAnnotationTypes注解:
@SupportedAnnotationTypes({"com.example.annotation.MyViewBinding"})
public class MyAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Messager messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE, "注解处理器运行了");
return true;
}
}
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SupportedAnnotationTypes {
String[] value();
}
可以看到也是返回一个String的集合,我们在集合中加入我们的注解的全类名
运行结果:
看到有一个关于version的警告,这个注解处理器默认使用RELEASE_6,而我用的是JAVA8所以有这个警告,可以把注解处理器的版本搞成RELEASE_8:
也是两种方法:
第一种实现getSupportedSourceVersion:
@SupportedAnnotationTypes({"com.example.annotation.MyViewBinding"})
public class MyAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Messager messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE, "注解处理器运行了");
return true;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_8;
}
}
运行结果没有那个警告了:
第二种方法用注解:
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({"com.example.annotation.MyViewBinding"})
public class MyAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Messager messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE, "注解处理器运行了");
return true;
}
}
运行结果:
同样没有警告了。
3.需要注册我们的注解处理器,在编译的时候让javac知道遇到我们MyViewBinding注解时要调起我们的注解处理器:
这个写法是固定的:
在main下面创建一个resources目录,然后在resource目录下创建一个META-INF目录,在META-INF目录下创建一个services目录,在services目录下创建一个javax.annotation.processing.Processor 文件,文件中放我们注解处理器的全类名:
com.example.compiler.MyAnnotationProcessor
这样一个自定义的注解处理器就能在编译时,发现代码中有要处理的自定义注解,就会执行它的process方法。
以上就是APT技术的简单使用
下面我们实战一下,用APT技术实现ButterKnife的功能: