新建两个module
annotation用来定义注解
compiler用来编写处理注解的代码
这两个module都要选择Java Library
那为什么要拆分两个module呢,因为编译期注解的处理代码是只在代码编译的时候使用的,所以这些代码要和主module分开拆成compiler,但是compiler又依赖于注解,主module也要使用注解。所以就将注解的定义也拆分出来。这样做的好处是可以在compiler中引入任何库,而不用考虑 Android 关于方法数的限制。例如Guava。
定义注解 @Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindViewCompiler {
int value() default -1;
}
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface OnClickCompiler {
int value() default -1;
}
复制代码
定义注解处理器 public class BindViewProcessor extends AbstractProcessor {
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public Set getSupportedAnnotationTypes() {
Set types = new LinkedHashSet<>();
types.add(BindViewCompiler.class.getCanonicalName());
types.add(OnClickCompiler.class.getCanonicalName());
return types;
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
return false;
}
}
复制代码
定义一个描述Java文件的类ClassModel public class ClassModel {
/**
* 成员变量
*/
private HashSet variableElements;
/**
* 类方法
*/
private HashSet executableElements;
/**
* 包
*/
private PackageElement packageElement;
/**
* 类
*/
private TypeElement classElement;
public ClassModel(TypeElement classElement) {
this.classElement = classElement;
packageElement = (PackageElement) classElement.getEnclosingElement();
variableElements = new HashSet<>();
executableElements = new HashSet<>();
}
public void addVariableElement(VariableElement element) {
variableElements.add(element);
}
public void addExecutableElement(ExecutableElement element) {
executableElements.add(element);
}
/**
* 生成Java文件
*/
public void generateJavaFile(Filer filer) {
}
}
复制代码
实现process方法 private HashMap classMap;
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 因为扫描会有多轮,所以需要清空一下,classMap在init方法中初始化
classMap.clear();
for (Element element : roundEnvironment.getElementsAnnotatedWith(BindViewCompiler.class)) {
ClassModel model = checkModel(element);
model.addVariableElement((VariableElement) element);
}
for (Element element : roundEnvironment.getElementsAnnotatedWith(OnClickCompiler.class)) {
ClassModel model = checkModel(element);
model.addExecutableElement((ExecutableElement) element);
}
for (ClassModel model : classMap.values()) {
model.generateJavaFile(processingEnv.getFiler());
}
return true;
}
private ClassModel checkModel(Element element) {
// 获取当前类
TypeElement classElement = (TypeElement) element.getEnclosingElement();
String qualifiedName = classElement.getQualifiedName().toString();
// 查看是否已经保存在classMap中了,如果没有就新创建一个
ClassModel model = classMap.get(qualifiedName);
if (model == null) {
model = new ClassModel(classElement);
classMap.put(qualifiedName, model);
}
return model;
}
复制代码
实现generateJavaFile方法 /**
* 生成Java文件
*/
public void generateJavaFile(Filer filer) {
try {
JavaFileObject jfo = filer.createSourceFile(classElement.getQualifiedName() + "$$view_binding");
BufferedWriter bw = new BufferedWriter(jfo.openWriter());
bw.append("package ").append(packageElement.getQualifiedName()).append(";\n");
bw.newLine();
bw.append(getImportString());
bw.newLine();
bw.append("public class ").append(classElement.getSimpleName()).append("$$view_binding implements Injectable {\n");
bw.newLine();
bw.append(getFiledString());
bw.newLine();
bw.append(getConstructString());
bw.newLine();
bw.append("}");
bw.flush();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 生成import代码
*/
private String getImportString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("import android.view.View;\n");
stringBuilder.append("import com.example.hao.learnself.date_2018_12_28.Injectable;\n");
stringBuilder.append("import ").append(classElement.getQualifiedName()).append(";\n");
HashSet importStrs = new HashSet<>();
for (VariableElement element : variableElements) {
importStrs.add("import " + element.asType().toString() + ";\n");
}
for (String str : importStrs) {
stringBuilder.append(str);
}
return stringBuilder.toString();
}
/**
* 生成成员变量
*/
private String getFiledString() {
return "private " + classElement.getSimpleName().toString() + " target;\n";
}
/**
* 生成构造函数
*/
private String getConstructString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("public ").append(classElement.getSimpleName().toString()).append("$$view_binding")
.append("(").append(classElement.getSimpleName()).append(" target, ").append("View view) {\n");
stringBuilder.append("this.target = target;\n");
for (VariableElement element : variableElements) {
int resId = element.getAnnotation(BindViewCompiler.class).value();
stringBuilder.append("target.").append(element.getSimpleName()).append(" = (").append(element.asType().toString())
.append(")view.findViewById(").append(resId).append(");\n");
}
for (ExecutableElement element : executableElements) {
int resId = element.getAnnotation(OnClickCompiler.class).value();
stringBuilder.append("view.findViewById(").append(resId).append(").setOnClickListener(new View.OnClickListener() {\n")
.append("@Override\n").append("public void onClick(View v) {\n")
.append("target.").append(element.getSimpleName()).append("();\n")
.append("}\n});\n");
}
stringBuilder.append("}");
return stringBuilder.toString();
}
复制代码
注册Processor。 Processor需要注册一下才能被注解处理器处理,在src/main/resources/META-INF/services下创建一个javax.annotation.processing.Processor文件,如果没有当前目录就新建一个
在该文件中写入Processor的全类名 com.example.compiler.BindViewProcessor
复制代码
build一下并查看生成的文件,在/app/build/generated/source/apt下 package com.example.hao.learnself.date_2018_12_28;
import android.view.View;
import com.example.hao.learnself.date_2018_12_28.Injectable;
import com.example.hao.learnself.date_2018_12_28.AnnotationTestActivity;
import android.widget.TextView;
public class AnnotationTestActivity$$view_binding implements Injectable {
private AnnotationTestActivity target;
public AnnotationTestActivity$$view_binding(AnnotationTestActivity target, View view) {
this.target = target;
target.compileSumTv = (android.widget.TextView)view.findViewById(2131165231);
target.compileAddBtn = (android.widget.TextView)view.findViewById(2131165230);
view.findViewById(2131165230).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
target.compileAdd();
}
});
}
}
复制代码
定义Injectable接口和Injection工具类 public interface Injectable {
}
public class Injection {
private static final String SUFFIX = "$$view_binding";
public static void inject(@NonNull Activity target) {
inject(target, target.getWindow().getDecorView());
}
public static void inject(@NonNull Object target, @NonNull View view) {
String className = target.getClass().getName();
try {
// 通过反射创建
Class> clazz = target.getClass().getClassLoader().loadClass(className + SUFFIX);
Constructor constructor = (Constructor) clazz.getConstructor(target.getClass(), View.class);
constructor.newInstance(target, view);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
复制代码
在测试页面中使用 public class AnnotationTestActivity extends BaseActivity {
@BindViewRuntime(R.id.runtime_add_btn)
private TextView runtimeAddBtn;
@BindViewRuntime(R.id.runtime_sum_tv)
private TextView runtimeSumTv;
@BindViewCompiler(R.id.compile_add_btn)
TextView compileAddBtn;
@BindViewCompiler(R.id.compile_sum_tv)
TextView compileSumTv;
@Override
protected int getLayoutId() {
return R.layout.activity_annotation_test;
}
@Override
protected void initView() {
Injection.inject(this);
}
@OnClickRuntime(R.id.runtime_add_btn)
void runTimeAdd() {
String text = runtimeSumTv.getText().toString();
runtimeSumTv.setText(String.valueOf(Integer.parseInt(text) + 1));
}
@OnClickCompiler(R.id.compile_add_btn)
void compileAdd() {
String text = compileSumTv.getText().toString();
compileSumTv.setText(String.valueOf(Integer.parseInt(text) + 1));
}
}
复制代码
查看效果