注解java-library
build.gradle
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
// 中文乱码问题(错误: 编码GBK的不可映射字符)
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
sourceCompatibility = "7"
targetCompatibility = "7"
BindView 注解
// SOURCE 注解仅在源码中保留,class文件中不存在
// CLASS 注解在源码和class文件中都存在,但运行时不存在
// RUNTIME 注解在源码,class文件中存在且运行时可以通过反射机制获取到
@Target(ElementType.FIELD) // 注解作用在属性之上
@Retention(RetentionPolicy.CLASS) // 编译期原理(交予注解处理器)
public @interface BindView {
// 返回R.id.xx值
int value();
}
OnClick 注解
@Target(ElementType.METHOD) // 注解作用在方法之上
@Retention(RetentionPolicy.CLASS) // 编译期原理(交予注解处理器)
public @interface OnClick {
// 此处省略了int[]
int value();
}
注解使用的实体类或者接口
ViewBinder 接口
/**
* 接口绑定类(所有注解处理器生的类,都需要实现该接口,= 接口实现类)
*/
public interface ViewBinder<T> {
void bind(T target);
}
DebouncingOnClickListener 抽象类
// 点击监听接口,实现类(抽象类 + 抽象方法)
public abstract class DebouncingOnClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
// 调用抽象方法
doClick(v);
}
public abstract void doClick(View v);
}
ButterKnife 核心类
/**
* 核心类,接口 = 接口实现类
* ButterKnife用的是构造方法.newInstance()
* 接口.bind()
*/
public class ButterKnife {
public static void bind(Activity activity) {
// 拼接类名,如:MainActivity$ViewBinder
String className = activity.getClass().getName() + "$ViewBinder";
try {
// 加载上述拼接类
Class<?> viewBinderClass = Class.forName(className);
// 接口 = 接口实现类
ViewBinder viewBinder = (ViewBinder) viewBinderClass.newInstance();
// 调用接口方法
viewBinder.bind(activity);
} catch (Exception e) {
e.printStackTrace();
}
}
}
注解处理器java-library
build.gradle
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// As3.5 + gradle5.4.1-all
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"
// 引入annotation,处理@BindView、@Onclick注解
implementation project(':annotation')
}
// 中文乱码问题(错误: 编码GBK的不可映射字符)
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
sourceCompatibility = "7"
targetCompatibility = "7"
ButterKnifeProcess 注解处理核心类
// 用来生成 META-INF/services/javax.annotation.processing.Processor 文件
@AutoService(Processor.class)
// 允许/支持的注解类型,让注解处理器处理
@SupportedAnnotationTypes({Constants.BINDVIEW_ANNOTATION_TYPES, Constants.ONCLICK_ANNOTATION_TYPES})
// 指定JDK编译版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class ButterKnifeProcess extends AbstractProcessor {
// 操作Element工具类 (类、函数、属性都是Element)
private Elements elementUtils;
// type(类信息)工具类,包含用于操作TypeMirror的工具方法
private Types typeUtils;
// Messager用来报告错误,警告和其他提示信息
private Messager messager;
// 文件生成器 类/资源,Filter用来创建新的类文件,class文件以及辅助文件
private Filer filer;
// key:类节点, value:被@BindView注解的属性集合
private Map<TypeElement, List<VariableElement>> tempBindViewMap = new HashMap<>();
// key:类节点, value:被@OnClick注解的方法集合
private Map<TypeElement, List<ExecutableElement>> tempOnClickMap = new HashMap<>();
// 该方法主要用于一些初始化的操作,通过该方法的参数ProcessingEnvironment可以获取一些列有用的工具类
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
// 初始化
elementUtils = processingEnvironment.getElementUtils();
typeUtils = processingEnvironment.getTypeUtils();
messager = processingEnvironment.getMessager();
filer = processingEnvironment.getFiler();
messager.printMessage(Diagnostic.Kind.NOTE,
"注解处理器初始化完成,开始处理注解------------------------------->");
}
/**
* 相当于main函数,开始处理注解
* 注解处理器的核心方法,处理具体的注解,生成Java文件
*
* @param set 使用了支持处理注解的节点集合
* @param roundEnvironment 当前或是之前的运行环境,可以通过该对象查找的注解。
* @return true 表示后续处理器不会再处理(已经处理完成)
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 一旦有属性上使用@BindView注解
if (!EmptyUtils.isEmpty(set)) {
// 获取所有被 @BindView 注解的 元素集合
Set<? extends Element> bindViewElements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
// 获取所有被 @OnClick 注解的 元素集合
Set<? extends Element> onClickElements = roundEnvironment.getElementsAnnotatedWith(OnClick.class);
if (!EmptyUtils.isEmpty(bindViewElements) || !EmptyUtils.isEmpty(onClickElements)) {
try {
// 赋值临时map存储,用来存放被注解的属性集合
valueOfMap(bindViewElements, onClickElements);
// 生成类文件,如:
createJavaFile();
return true;
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
private void createJavaFile() throws IOException {
// 判断是否有需要生成的类文件
if (!EmptyUtils.isEmpty(tempBindViewMap)) {
// 获取ViewBinder接口类型(生成类文件需要实现的接口)
TypeElement viewBinderType = elementUtils.getTypeElement(Constants.VIEWBINDER);
TypeElement clickListenerType = elementUtils.getTypeElement(Constants.CLICKLISTENER);
TypeElement viewType = elementUtils.getTypeElement(Constants.VIEW);
for (Map.Entry<TypeElement, List<VariableElement>> entry : tempBindViewMap.entrySet()) {
// 类名
ClassName className = ClassName.get(entry.getKey());
// 实现接口泛型
ParameterizedTypeName typeName = ParameterizedTypeName.get(ClassName.get(viewBinderType),
ClassName.get(entry.getKey()));
// 参数体配置(MainActivity target)
ParameterSpec parameterSpec = ParameterSpec.builder(ClassName.get(entry.getKey()), // MainActivity
Constants.TARGET_PARAMETER_NAME) // target
.addModifiers(Modifier.FINAL)
.build();
// 方法配置:public void bind(MainActivity target) {
MethodSpec.Builder methodBuidler = MethodSpec.methodBuilder(Constants.BIND_METHOD_NAME) // 方法名
.addAnnotation(Override.class) // 重写注解
.addModifiers(Modifier.PUBLIC) // public修饰符
.addParameter(parameterSpec); // 方法参数
for (Element fieldElement : entry.getValue()) {
// 获取属性名
String fieldName = fieldElement.getSimpleName().toString();
// 获取@BindView注解的值
int annotationValue = fieldElement.getAnnotation(BindView.class).value();
// target.tv = target.findViewById(R.id.tv);
String methodContent = "$N." + fieldName + " = $N.findViewById($L)";
methodBuidler.addStatement(methodContent,
Constants.TARGET_PARAMETER_NAME,
Constants.TARGET_PARAMETER_NAME,
annotationValue);
}
if (!EmptyUtils.isEmpty(tempOnClickMap)) {
for (Map.Entry<TypeElement, List<ExecutableElement>> methodEntry : tempOnClickMap.entrySet()) {
// 类名
if (className.equals(ClassName.get(entry.getKey()))) {
for (ExecutableElement methodElement : methodEntry.getValue()) {
// 获取方法名
String methodName = methodElement.getSimpleName().toString();
// 获取@OnClick注解的值
int annotationValue = methodElement.getAnnotation(OnClick.class).value();
/**
* target.findViewById(2131165312).setOnClickListener(new DebouncingOnClickListener() {
* public void doClick(View view) {
* target.click(view);
* }
* });
*/
methodBuidler.beginControlFlow("$N.findViewById($L).setOnClickListener(new $T()",
Constants.TARGET_PARAMETER_NAME, annotationValue, ClassName.get(clickListenerType))
.beginControlFlow("public void doClick($T view)", ClassName.get(viewType))
.addStatement("$N." + methodName + "(view)", Constants.TARGET_PARAMETER_NAME)
.endControlFlow()
.endControlFlow(")")
.build();
}
}
}
}
// 必须是同包(属性修饰符缺省),MainActivity$$ViewBinder
JavaFile.builder(className.packageName(), // 包名
TypeSpec.classBuilder(className.simpleName() + "$ViewBinder") // 类名
.addSuperinterface(typeName) // 实现ViewBinder接口
.addModifiers(Modifier.PUBLIC) // public修饰符
.addMethod(methodBuidler.build()) // 方法的构建(方法参数 + 方法体)
.build()) // 类构建完成
.build() // JavaFile构建完成
.writeTo(filer); // 文件生成器开始生成类文件
}
}
}
private void valueOfMap(Set<? extends Element> bindViewElements, Set<? extends Element> onClickElements) {
if (!EmptyUtils.isEmpty(bindViewElements)) {
for (Element element : bindViewElements) {
messager.printMessage(Diagnostic.Kind.NOTE, "@BindView >>> " + element.getSimpleName());
if (element.getKind() == ElementKind.FIELD) {
VariableElement fieldElement = (VariableElement) element;
// 注解在属性之上,属性节点父节点是类节点
TypeElement enclosingElement = (TypeElement) fieldElement.getEnclosingElement();
// 如果map集合中的key:类节点存在,直接添加属性
if (tempBindViewMap.containsKey(enclosingElement)) {
tempBindViewMap.get(enclosingElement).add(fieldElement);
} else {
List<VariableElement> fields = new ArrayList<>();
fields.add(fieldElement);
tempBindViewMap.put(enclosingElement, fields);
}
}
}
}
if (!EmptyUtils.isEmpty(onClickElements)) {
for (Element element : onClickElements) {
messager.printMessage(Diagnostic.Kind.NOTE, "@OnClick >>> " + element.getSimpleName());
if (element.getKind() == ElementKind.METHOD) {
ExecutableElement methodElement = (ExecutableElement) element;
// 注解在属性之上,属性节点父节点是类节点
TypeElement enclosingElement = (TypeElement) methodElement.getEnclosingElement();
// 如果map集合中的key:类节点存在,直接添加属性
if (tempOnClickMap.containsKey(enclosingElement)) {
tempOnClickMap.get(enclosingElement).add(methodElement);
} else {
List<ExecutableElement> fields = new ArrayList<>();
fields.add(methodElement);
tempOnClickMap.put(enclosingElement, fields);
}
}
}
}
}
}
Constants 常量类
/**
* 常量类
*/
public class Constants {
// 注解处理器中支持的注解类型
public static final String BINDVIEW_ANNOTATION_TYPES = "com.sanguine.butterknife.annotation.BindView";
public static final String ONCLICK_ANNOTATION_TYPES = "com.sanguine.butterknife.annotation.OnClick";
// 布局、控件绑定实现接口
public static final String VIEWBINDER = "com.sanguine.butterknife.library.ViewBinder";
public static final String CLICKLISTENER = "com.sanguine.butterknife.library.DebouncingOnClickListener";
public static final String VIEW = "android.view.View";
// bind方法名
public static final String BIND_METHOD_NAME = "bind";
// bind方法的参数名target
public static final String TARGET_PARAMETER_NAME = "target";
}
EmptyUtils 判空类
/**
* 字符串、集合判空工具
*/
public final class EmptyUtils {
public static boolean isEmpty(CharSequence cs) {
return cs == null || cs.length() == 0;
}
public static boolean isEmpty(Collection<?> coll) {
return coll == null || coll.isEmpty();
}
public static boolean isEmpty(final Map<?, ?> map) {
return map == null || map.isEmpty();
}
}
总结
本文通过APT+JavaPoet技术实现编译器解析注解,并且生成Java文件。APT负责解析注解,JavaPoet负责生成Java文件。最终实现了ButterKnife。在之前组件化的时候,我们同样有用过这样的技术,当时是通过生成的问题实现组件化之间的通信。