注解处理器->02源码注释
2019-05-31 本文已影响0人
冉桓彬
源码: https://blog.csdn.net/u013045971/article/details/53509237
文档: https://www.jianshu.com/p/9177db78cfed
注解处理器如果说难, 可能只是难在api上面, 由于对api的不熟, 而且网上也不太容易搜索相关的答案, 所以可能觉得难.
切记:
不要背api, 如果工作中涉及到需要自己去写相关代码, 只需要记得哪里有相关的实现, 然后直接复制粘贴拿来使用即可.
下面对网上别人写的现成的demo进行注释, 完成之后打算进阶对ButterKnife以及ARouter注解处理器相关源码进行注释.
一、AptActivity
public class AptActivity extends Activity {
@BindView(R.id.btn1)
public Button btn1;
@BindView(R.id.btn2)
public Button btn2;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_apt);
ButterKnife.bind(this);
btn1.setText("按钮1");
btn2.setText("按钮2");
}
@OnClick({R.id.btn1})
public void click1() {
Log.v("AndroidTest", "btn1-click");
}
@OnClick({R.id.btn2})
public void click2() {
Log.v("AndroidTest", "btn2-click");
}
}
二、AptActivity$$Injector这段代码在查看AnnotatedClass代码时再来对照的查看
public class AptActivity$$Injector implements Injector<AptActivity> {
@Override
public void inject(final AptActivity host, Object source, Finder finder) {
host.btn1=(Button)finder.findView(source, 2131427463);
host.btn2=(Button)finder.findView(source, 2131427464);
View.OnClickListener listener;
listener = new View.OnClickListener() {
@Override
public void onClick(View view) {
host.click1();
}
};
finder.findView(source, 2131427463).setOnClickListener(listener);
listener = new View.OnClickListener() {
@Override
public void onClick(View view) {
host.click2();
}
};
finder.findView(source, 2131427464).setOnClickListener(listener);
}
}
三、AbstractProcessor
@AutoService(Processor.class)
public class BindViewProcessor extends AbstractProcessor {
// 文件相关的辅助类
private Filer filer;
// 元素相关的辅助类
private Elements elementUtils;
// 日志相关的辅助类
private Messager messager;
// 解析的目标注解集合
private Map<String, AnnotatedClass> annotatedClassMap = new HashMap<>();
private Logger logger;
// ProcessingEnvironment提供一系列的工具类
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
// 作为工具类, 提供对应的Element.
elementUtils = processingEnv.getElementUtils();
// 用于编译时日志的输出.
messager = processingEnv.getMessager();
// 文件相关的辅助类, 后边生成对应的java文件时需要使用到.
filer = processingEnv.getFiler();
logger = new Logger(messager);
}
@Override
public Set<String> getSupportedAnnotationTypes() {
logger.info("BindView.class.getCanonicalName():" + BindView.class.getCanonicalName());
logger.info("OnClick.class.getCanonicalName():" + OnClick.class.getCanonicalName());
Set<String> types = new LinkedHashSet<>();
types.add(BindView.class.getCanonicalName());
types.add(OnClick.class.getCanonicalName());
return types;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
// RoundEnvironment: 运行环境, 通过该对象可以获取被指定注解标注的元素的集合.
@Override
public boolean process(Set<? extends TypeElement> anno, RoundEnvironment roundEnv) {
annotatedClassMap.clear();
try {
// 获取被BindView注解的元素的集合.
processBindView(roundEnv);
// 获取被OnClick注解的元素的集合.
processOnClick(roundEnv);
} catch (IllegalArgumentException e) {
return true;
}
try {
for (AnnotatedClass annotatedClass : annotatedClassMap.values()) {
annotatedClass.setLogger(logger);
logger.info("annotatedClass:" + annotatedClass + "\nannotatedClass.getFullClassName():" + annotatedClass.getFullClassName());
JavaFile javaFile = annotatedClass.generateFinder();
// 这里添加打印然后查看javaFile输出的内容
logger.info("javaFile:" + javaFile + ",mFiler:" + filer);
javaFile.writeTo(filer);
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
private void processBindView(RoundEnvironment roundEnv) {
// 这里的元素集合就是AptActivity中的btn1与btn2对应的元素:VariableElement.
for (Element element : roundEnv.getElementsAnnotatedWith(BindView.class)) {
AnnotatedClass annotatedClass = getAnnotatedClass(element);
BindViewField field = new BindViewField(element);
logger.info("processBindView()---element:" + element + ",field:" + field);
annotatedClass.addField(field);
}
}
private AnnotatedClass getAnnotatedClass(Element element) {
// 1.通过getEnclosingElement可以获取当前Element对应源代码中的父类型元素. 这里指的就是AptActivity
// 对应的元素类型.
// 2.只能通过TypeElement获取到对应的类名称, 但是不能获取类信息.
// 3.如果需要获取类相关的信息, 可以通过typeElement.asType()获取对应的TypeMirror.
TypeElement encloseElement = (TypeElement) element.getEnclosingElement();
// 获取对应的类名称.
String fullClassName = encloseElement.getQualifiedName().toString();
// 一个fullClassName对应一个AnnotatedClass.
AnnotatedClass annotatedClass = annotatedClassMap.get(fullClassName);
if (annotatedClass == null) {
annotatedClass = new AnnotatedClass(encloseElement, elementUtils);
annotatedClassMap.put(fullClassName, annotatedClass);
}
return annotatedClass;
}
private void processOnClick(RoundEnvironment roundEnv) {
// 这里的元素集合就是AptActivity中的click1()与click2()对应的元素:ExecuteableElement
for (Element element : roundEnv.getElementsAnnotatedWith(OnClick.class)) {
AnnotatedClass annotatedClass = getAnnotatedClass(element);
OnClickMethod method = new OnClickMethod(element);
annotatedClass.addMethod(method);
}
}
}
四、BindViewField
public class BindViewField {
// VariableElement:表示一个字段、enum常量、方法或构造方法参数、局部变量或异常参数.
// BindView注解针对的就是变量, 所以这里会将传入的element强转为VariableElement.
private VariableElement mFieldElement;
// 获取对应View的id.
private int mResId;
public BindViewField(Element element) throws IllegalArgumentException {
// 判断对应元素的类型是否为FIELD类型.
if (element.getKind() != ElementKind.FIELD) {
...
}
mFieldElement = (VariableElement) element;
BindView bindView = mFieldElement.getAnnotation(BindView.class);
mResId = bindView.value();
}
// 获取被BindView注解的对象的变量名.
public Name getFieldName() {
return mFieldElement.getSimpleName();
}
public int getResId() {
return mResId;
}
// 因为通过Element无法获取对应元素的具体信息, 所以需要通过asType获取对应的TypeMirror, 然后
// 根据TypeMirror获取对应的元素信息.
public TypeMirror getFieldType() {
return mFieldElement.asType();
}
}
五、OnClickMethod同BindViewField , 这里不再分析
public class OnClickMethod {
private Name methodName;
public int[] ids;
public OnClickMethod(Element element) {
if (element.getKind() != ElementKind.METHOD) {
throw new IllegalArgumentException(String.format("Only method can be annotated width @%s",
OnClick.class.getSimpleName()));
}
ExecutableElement methodElement = (ExecutableElement) element;
methodName = methodElement.getSimpleName();
ids = methodElement.getAnnotation(OnClick.class).value();
if (ids == null) {
throw new IllegalArgumentException(String.format("Must set valid ids for @%s", OnClick.class.getSimpleName()));
} else {
for (int id : ids) {
if (id < 0) {
throw new IllegalArgumentException(String.format("Must set valid ids for @%s", OnClick.class.getSimpleName()));
}
}
}
List<? extends VariableElement> parameters = methodElement.getParameters();
if (parameters.size() > 0) {
throw new IllegalArgumentException(String.format("The method annotated with @%s must have no parameters", OnClick.class.getSimpleName()));
}
}
public Name getMethodName() {
return methodName;
}
}
六、AnnotatedClass
public class AnnotatedClass {
// 类元素
public TypeElement mClassElement;
// 成员变量: 持有btn1、btn2对应的BindViewField
public List<BindViewField> mFiled;
// 方法: 持有click1()、click2()对应的OnClickMethod
public List<OnClickMethod> mMethod;
// 元素帮助类
public Elements mElementUtils;
// 在BindViewProcessor中可知, 一个fullClassName对应一个AnnotatedClass.
public AnnotatedClass(TypeElement classElement, Elements elementUtils) {
this.mClassElement = classElement;
this.mElementUtils = elementUtils;
this.mFiled = new ArrayList<>();
this.mMethod = new ArrayList<>();
}
public String getFullClassName() {
return mClassElement.getQualifiedName().toString();
}
public void addField(BindViewField field) {
mFiled.add(field);
}
public void addMethod(OnClickMethod method) {
mMethod.add(method);
}
// 生成对应的java文件
public JavaFile generateFinder() {
/***构建方法*/
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("inject") //方法名称
.addModifiers(Modifier.PUBLIC) // 方法访问权限
.addAnnotation(Override.class) // 方法的注解
.returns(TypeName.VOID) //方法的返回值
.addParameter(TypeName.get(mClassElement.asType()), "host", Modifier.FINAL) // 方法参数
.addParameter(TypeName.OBJECT, "source") // 方法参数
.addParameter(TypeUtil.FINDER, "finder"); // 方法参数
/*** 遍历添加类成员*/
for (BindViewField field : mFiled) {
logger.info("field.getFieldName:" + field.getFieldName() + ", field.getFieldType:" + field.getFieldType());
// methodBuilder方法内部的执行语句
methodBuilder.addStatement("host.$N=($T)finder.findView(source,$L)",
field.getFieldName(),
ClassName.get(field.getFieldType()),
field.getResId());
}
/*** 声明Listener*/
if (mMethod.size() > 0) {
logger.info("TypeUtil.ONCLICK_LISTENER:" + TypeUtil.ONCLICK_LISTENER);
// methodBuilder方法内部的执行语句
methodBuilder.addStatement("$T listener", TypeUtil.ONCLICK_LISTENER);
}
for (OnClickMethod method : mMethod) {// click1()、click2()
TypeSpec listener = TypeSpec.anonymousClassBuilder("")
// 为当前Listener添加父类或者父接口, 这里使用的是匿名内部类的方式
.addSuperinterface(TypeUtil.ONCLICK_LISTENER)
.addMethod(MethodSpec.methodBuilder("onClick") // 定义方法名
.addAnnotation(Override.class) // 定义方法的继承关系
.addModifiers(Modifier.PUBLIC) // 定义方法的访问权限
.returns(TypeName.VOID) // 定义方法的返回值
.addParameter(TypeUtil.ANDROID_VIEW, "view") // 定义方法的参数
.addStatement("host.$N()", method.getMethodName()) // 定义方法内部的语句
.build())
.build();
methodBuilder.addStatement("listener = $L ", listener);
for (int id : method.ids) {
methodBuilder.addStatement("finder.findView(source,$L).setOnClickListener(listener)", id);
}
}
// 定义包名
String packageName = getPackageName(mClassElement);
// 定义类名
String className = getClassName(mClassElement, packageName);
ClassName bindClassName = ClassName.get(packageName, className);
/*** 构建类*/
TypeSpec finderClass = TypeSpec.classBuilder(bindClassName.simpleName() + "$$Injector")
.addModifiers(Modifier.PUBLIC) // 定义类的访问权限
// 为当前类添加父类或者父接口
.addSuperinterface(ParameterizedTypeName.get(TypeUtil.INJECTOR, TypeName.get(mClassElement.asType())))
.addMethod(methodBuilder.build()) // 将methodBuilder添加到类中
.build();
// 构建对应的java文件, 但是此时还没有生成对应的java文件
return JavaFile.builder(packageName, finderClass).build();
}
public String getPackageName(TypeElement type) {
return mElementUtils.getPackageOf(type).getQualifiedName().toString();
}
private static String getClassName(TypeElement type, String packageName) {
int packageLen = packageName.length() + 1;
return type.getQualifiedName().toString().substring(packageLen).replace('.', '$');
}
private Logger logger;
public void setLogger(Logger logger) {
this.logger = logger;
}
}
六、Logger
public class Logger {
private Messager messager;
public Logger(Messager messager) {
this.messager = messager;
}
public void info(String info) {
if (messager == null) {
return;
}
messager.printMessage(Diagnostic.Kind.NOTE, info);
}
}