什么是apt
APT(Annotation Processing Tool)即注解处理器,是一种处理注解的工具,确切的说它是javac的一个工具,它用来在编译时扫描和处理注解。注解处理器以Java代码(或者编译过的字节码)作为输入,生成.java文件作为输出。
简单来说就是在编译期,通过注解生成.java文件。
如果没接触过注解开发的同学可以看我之前的文章
Android注解--初探
apt 的作用
使用APT的优点就是方便、简单,可以少些很多重复的代码。
用过ButterKnife、Dagger、EventBus等注解框架的同学就能感受到,利用这些框架可以少些很多代码,只要写一些注解就可以了。
其实,他们不过是通过注解,生成了一些代码。
本文需求
通过APT实现一个功能,通过对View变量的注解,实现View的绑定(类似于ButterKnife中的@BindView)
创建项目
- 创建Android Module命名为app 依赖 apt_library
- 创建Java library Module命名为 apt_annotation
- 创建Java library Module命名为 apt_processor 依赖 apt_annotation
- 创建Android library Module 命名为 apt_library 依赖 apt_processor
注解开发需要创建Java library因为有些方法、类 Android library 中并不支持
Module职责
- apt_annotation:自定义注解,存放@BindView
- apt_processor:注解处理器,根据apt-annotation中的注解,在编译期生成xxxActivity_ViewBinding.java代码
- apt_library:工具类,调用xxxActivity_ViewBinding.java中的方法,实现View的绑定。
实现
其实有两种方式可以实现这个功能
- RetentionPolicy.CLASS 编译时注解
- RetentionPoicy.RUNTIME 运行时注解
在很多情况下,运行时注解和编译时注解可以实现相同的功能,比如依赖注入框架,我们既可以在运行时通过反射来初始化控件,也可以再编译时就生成控件初始化代码。那么,这两者有什么区别呢?
答:编译时注解性能比运行时注解好,运行时注解需要使用到反射技术,对程序的性能有一定影响,而编译时注解直接生成了源代码,运行过程中直接执行代码,没有反射这个过程。
实现一:RetentionPolicy.CLASS 编译时注解
1、创建注解类BindView
/**
* @author liuboyu E-mail:545777678@qq.com
* @Date 2019-08-21
* @Description BindView 注解定义
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
@Retention(RetentionPolicy.CLASS):表示编译时注解
@Target(ElementType.FIELD):表示注解范围为类成员(构造方法、方法、成员变量)
2、apt_processor(注解处理器)
在Module中添加依赖
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
api project(':apt_annotation')
}
创建BindViewProcessor
/**
* @author liuboyu E-mail:545777678@qq.com
* @Date 2019-08-21
* @Description 注解处理器
*/
public class BindViewProcessor extends AbstractProcessor {
private Messager mMessager;
private Elements mElementUtils;
private Map<String, ClassCreatorProxy> mProxyMap = new HashMap<>();
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mMessager = processingEnv.getMessager();
mElementUtils = processingEnv.getElementUtils();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
HashSet<String> supportTypes = new LinkedHashSet<>();
supportTypes.add(BindView.class.getCanonicalName());
return supportTypes;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_8;
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
mMessager.printMessage(Diagnostic.Kind.NOTE, "processing...");
mProxyMap.clear();
//获得被BindView注解标记的element
Set<? extends Element>