APT(Annotation Processing Tool
)即注解处理器,是一种注解处理的工具,用来在编译器扫描以及处理注解。以注解作为桥梁,通过预先设定好的代码规则来生成对应的Java代码。实际开发中有很多开源框架都是用到APT,例如:ButterKnife,Dagger2,EventBus,Spring框架等。
APT原理:在注解了某些代码元素(字段、函数、类)后,编译器检查
AbstractProcessor
的子类,自动调用process()
方法,将所有添加了指定注解的元素传递给该函数,在根据这些元素通过Java规则生成对应的Java代码。
现在我就简单的实现一个仿ButterKnife的功能。
我们先观察在ButterKnife中生成的注解绑定的代码,寻找其规律后仿造即可:
package com.wdl.aptdemo;
public class MainActivityViewBinding {
public static void bind(MainActivity _mainActivity) {
_mainActivity.btn = (android.widget.Button) (_mainActivity.findViewById(2131165218));
_mainActivity.textView = (android.widget.TextView) (_mainActivity.findViewById(2131165326));
}
}
规律:依照规律指定Java生成规则
apt_annotation
的Java Library(Moudle),用于存放定义的注解(创建过程省略)apt_processor
的Java Library(Moudle),并引用apt_annotation,用于处理注解以及生成Java代码apt_processor下
的build.gradle
中如下依赖:(看注释)apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// 注解注册处理器
implementation 'com.google.auto.service:auto-service:1.0-rc2'
// square 开源的代码生成框架,通过API生成指定格式(修饰符 返回值 参数 函数体)的代码
implementation 'com.squareup:javapoet:1.10.0'
implementation project(':apt_annotation')
}
sourceCompatibility = "7"
targetCompatibility = "7"
app/build.gradle
中添加依赖:implementation project(":apt_annotation")
annotationProcessor project(":apt_processor")
package com.wdl.apt_annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@SuppressWarnings("unused")
// 保留到编译阶段不加载进JVM
@Retention(RetentionPolicy.CLASS)
// 作用对象,FILED
@Target(ElementType.FIELD)
public @interface BindView {
int value(); // 属性名为value 的int变量
}
AbstractProcessor
子类的实现:
package com.wdl.apt_processor;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import com.wdl.apt_annotation.BindView;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;
@SuppressWarnings("unused")
@AutoService(Processor.class)
public class BindViewProcessor extends AbstractProcessor {
// Element代表程序的元素,例如包、类、方法。
private Elements elementUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
elementUtils = processingEnvironment.getElementUtils();
}
/**
* @return 指定java版本。
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* 指定该目标的注解对象,指定注解处理器是注册给哪个注解的,返回指定支持的注解类集合。
*
* @return Set getCanonicalName即包名.类名,不同的对象获取的值不同,可能为空
*/
@Override
public Set getSupportedAnnotationTypes() {
Set hashSet = new HashSet<>();
hashSet.add(BindView.class.getCanonicalName());
return hashSet;
}
/**
* 处理包含指定注解对象的代码元素
* 获取控件变量的引用以及对应的viewId,先遍历出每个Activity所包含的所有注解对象
*
* @param set Set extends TypeElement>
* @param roundEnvironment RoundEnvironment 所有注解的集合
* @return true
*/
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 获取所有包含BindView注解的元素
Set extends Element> elementSet = roundEnvironment.getElementsAnnotatedWith(BindView.class);
// 此处的TypeElement就是Activity
// Activity中包含的 id以及对应的属性(控件)
Map> typeElementMapHashMap = new HashMap<>();
for (Element element : elementSet) {
// 注解的是FIELD,因此可以直接转换
VariableElement variableElement = (VariableElement) element;
// 获取最里层的元素,此处就是Activity
TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
// 获取对应Activity中的Map viewId View
Map variableElementMap = typeElementMapHashMap.get(typeElement);
if (variableElementMap == null) {
variableElementMap = new HashMap<>();
typeElementMapHashMap.put(typeElement, variableElementMap);
}
// 获取注解对象
BindView bindView = variableElement.getAnnotation(BindView.class);
// 获取注解值
int id = bindView.value();
variableElementMap.put(id, variableElement);
}
for (TypeElement key : typeElementMapHashMap.keySet()) {
Map elementMap = typeElementMapHashMap.get(key);
String packageName = elementUtils.getPackageOf(key).getQualifiedName().toString();
JavaFile javaFile = JavaFile.builder(packageName, generateCodeByPoet(key,
elementMap)).build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
private TypeSpec generateCodeByPoet(TypeElement typeElement, Map variableElementMap) {
//自动生成的文件以 Activity名 + ViewBinding 进行命名
return TypeSpec.classBuilder(typeElement.getSimpleName().toString() + "ViewBinding")
.addModifiers(Modifier.PUBLIC)
.addMethod(generateMethodByPoet(typeElement, variableElementMap))
.build();
}
/**
* @param typeElement 注解对象的根元素,即Activity
* @param variableElementMap Activity包含的注解对象以及注解的目标对象
* @return MethodSpec
*/
private MethodSpec generateMethodByPoet(TypeElement typeElement, Map variableElementMap) {
ClassName className = ClassName.bestGuess(typeElement.getQualifiedName().toString());
// _mainActivity.btn_serializeSingle = (android.widget.Button) (_mainActivity.findViewById(2131165221));
// 第一个转小写+下划线
String parameter = "_" + Utils.toLowerCaseFirstChar(className.simpleName());
MethodSpec.Builder builder = MethodSpec.methodBuilder("bind") // 方法名
.addModifiers(Modifier.PUBLIC, Modifier.STATIC) // public static
.returns(void.class)// 返回类型
.addParameter(className, parameter);
for (int viewId : variableElementMap.keySet()) {
VariableElement variableElement = variableElementMap.get(viewId);
// 变量名
String fieldName = variableElement.getSimpleName().toString();
// 变量父类的全称
String fieldType = variableElement.asType().toString();
String text = "{0}.{1} = ({2})({3}.findViewById({4}));";
builder.addCode(MessageFormat.format(text, parameter, fieldName, fieldType, parameter, String.valueOf(viewId)));
}
return builder.build();
}
}
详细方法:
getSupportedAnnotationTypes()
:返回该类指定注解的对象generateCodeByPoet()
:生成对应的类generateMethodByPoet
:生成对应方法如何使用?与ButterKnife类似。
public class MainActivity extends AppCompatActivity {
@BindView(R.id.btn_btn)
Button btn;
@BindView(R.id.tv_t)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainActivityViewBinding.bind(this);
//textView.setText("Succeed.....................");
}
}
我的Github,大家Start
参考:https://www.jianshu.com/p/cc8379522c5e