本文已参与「新人创作礼」活动,一起开启掘金创作之路。
什么是APT?
编译时注解,就是在编译期间对源文件检查,并找出指定的注解,然后根据注解生成新的源文件,最终和原来的源文件共同被编译成class文件,具体流程如下(图摘抄自Android 注解系列之APT工具(三)):
自己的例子
使用
- gradle中引用
dependencies {
...
implementation project(":BindEvent")
annotationProcessor project(":annotation_compiler")
}
- Activity中的使用
public class MainActivity extends AppCompatActivity {
// 2. 在字段名上直接注解使用
@BindView(R.id.tv_test)
TextView tvTest;
@BindView(R.id.btn_main)
Button btnMain;
@BindView(R.id.btn1_main)
Button btn1Main;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1. 在oncreate中绑定布局
BindEvent.bind(this);
}
}
效果
make项目就会自动生成对应代码,如下图:
例子下载
开发步骤
1.新建一个名为annotation的Java的library库,并新建一个注解,如下:
2.新建一个名为compiler的Java的library库,并在build.gradle中添加一下代码
dependencies {
...
// 注解声明库
implementation project(":annotation")
// 用来注册自定义注解处理器(可选,如果使用就引入)
compileOnly "com.google.auto.service:auto-service:1.0-rc6"
annotationProcessor "com.google.auto.service:auto-service:1.0-rc6"
// 用来生成Java代码
implementation "com.squareup:javapoet:1.10.0"
}
3.在compiler的库中新建一个类并继承AbstractProcessor,并实现其方法,主要代码如下
/**
* 注解处理器 解析指定注解并生成Java 代码
*/
//@AutoService(Processor.class)
public class AnnotationProcessor extends AbstractProcessor {
/**
* @param set
* @param roundEnvironment 携带了项目中 所有这个注解处理器声明了的 注解 所标注的 内容
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
// process 方法会执行多次
// 但是 set里面有数据的只会执行一次
if (set.iterator().hasNext()) {
// 拿到所有的BindView 并且根据上层节点分开的map
Map<TypeElement, List<Element>> map = ParseTargets(roundEnvironment);
// 根据map里的数据分别生成 Java文件
if (map.size() > 0) {
for (TypeElement typeElement : map.keySet()) {
// 创造 方法(构造是方法的一种)
MethodSpec.Builder builder = MethodSpec.constructorBuilder();
// 添加参数
// builder.addParameter(TypeVariableName.get(packageName + "." + activityName), "target");
builder.addParameter(TypeVariableName.get(typeElement.getQualifiedName().toString()), "target");
builder.addParameter(ClassName.get("android.view", "View"), "view");
for (Element e : map.get(typeElement)) {
String fieldName = e.getSimpleName().toString();
int id = e.getAnnotation(BindView.class).value();
// 设置数据
builder.addStatement("target.$L = view.findViewById($L)", fieldName, id);
}
MethodSpec methodSpec = builder.build();
String newName = typeElement.getSimpleName() + "_ViewBind";
//获取包名
String packageName = getPackageName(typeElement);
// 创建类
TypeSpec typeSpec = TypeSpec.classBuilder(newName)
.addModifiers(Modifier.PUBLIC)
.addMethod(methodSpec)
.build();
JavaFile javaFile = JavaFile.builder(packageName, typeSpec).build();
try {
javaFile.writeTo(filer);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return false;
}
}
4.注册注解处理器(这里有两种方法)
-
方法一:按照下图新建目录和File,并在file中加入自己的processor的全路径:
-
方法二:使用注解
4.新建一个名为BindEvent的android的library库,并在build.gradle中添加以下依赖
dependencies {
...
api project(":annotation")
}
5. 在BindEvent库中新建一个BindEvent的Java类,代码如下
public class BindEvent {
public static void bind(Activity activity) {
View sourceView = activity.getWindow().getDecorView();
bind(activity, sourceView);
}
public static void bind(Object o, View view) {
Class<?> aClass = o.getClass();
String newName = aClass.getPackage().getName() + "." + aClass.getSimpleName() + "_ViewBinder";
try {
// 根据反射原理来创建它的构造方法
Class<?> aClass1 = Class.forName(newName);
Constructor<?> constructor = aClass1.getConstructor(aClass, view.getClass());
constructor.setAccessible(false);
constructor.newInstance(o, view);
} catch (Exception e) {
e.printStackTrace();
}
}
}
到这里就已经开发完毕了