Java注解知多少之自定义注解处理器
前言
要知道如何自定义注解处理器(简称:APT),首先还是先得知道注解与注解处理器是怎么一会儿事儿,关于自定义注解,可以看我之前的文章,现在主要讲的是注解处理器的使用。
简单来说注解处理器其实就是用来处理注解用的,在编译的运行成class文件的时候,注解处理器会根据需要处理的注解执行一些操作。
下面我们来自定义个跟ButterKnife类似的注解处理器来了解他的大致用法。
一、自定义注解
https://www.jianshu.com/p/ff234438a87a
既然是要自定义注解处理器,自然少不了注解啦,首先创建一个java Module(butterknife_annotation
/**
* @author Gentle Wen
*/
@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Target(ElementType.FIELD)//作用域在成员变量
public @interface LsBindView {
int value();//注解需要填入的值,就是你的view id
}
二、自定义注解处理器
下面就是处理注解的APT,首先我们创建一个java Module 并先引用一些依赖包,当然包括上面创建的注解的module
dependencies {
//注解处理器需要用到的依赖,这里应用谷歌的
implementation 'com.google.auto.service:auto-service:1.0-rc6'
implementation project(':butterknife_annotation')
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
}
/**
* 创建日期:2020/5/30 0007
*
* @author :Gentle Wen
* describe:
*/
@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
/**
* 注解处理器元素工具类
*/
private Elements elementUtils;
/**
* 注解处理器操作相关的
*/
private Filer filer;
/**
* 注解处理器打印相关
*/
private Messager messager;
// private Map javaCodeCreatorMap = new HashMap<>();
/**
* 注解处理器支持的注解
*
* @return
*/
@Override
public Set getSupportedAnnotationTypes() {
Set set = new LinkedHashSet<>();
set.add(LsBindView.class.getCanonicalName());
return set;
}
/**
* jdk版本号
*
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* 初始化注解处理器的时候调用
*
* @param processingEnvironment
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
//元素的工具
elementUtils = processingEnvironment.getElementUtils();
//用code String生成一个java文件调用的类对象
filer = processingEnvironment.getFiler();
//打印
messager = processingEnvironment.getMessager();
}
/**
* 注解处理器的入口
*
* @param set
* @param roundEnvironment
* @return
*/
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
messager.printMessage(Diagnostic.Kind.NOTE,"process start....");
try {
JavaCodeCreator javaCodeCreator = null;
//得到有这个LsBindView.class所有元素
Set extends Element> elements = roundEnvironment.getElementsAnnotatedWith(LsBindView.class);
for (Element element : elements) {
//成员变量的元素
VariableElement variableElement = (VariableElement) element;
//Class节点
TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
//注解
LsBindView annotation = variableElement.getAnnotation(LsBindView.class);
//viewId
int viewId = annotation.value();
if (javaCodeCreator == null) {
//创建一个java代码生成的工具类
javaCodeCreator = new JavaCodeCreator(typeElement, elementUtils);
}
javaCodeCreator.saveVariableElement(viewId, variableElement);
}
if (javaCodeCreator != null) {
//在工具类中拼接完我们需要的代码
String code = javaCodeCreator.createCode();
//打印
messager.printMessage(Diagnostic.Kind.NOTE,code);
//利用该对象创建出我们需要的java文件
JavaFileObject sourceFile = filer.createSourceFile(javaCodeCreator.getFullClassName(), javaCodeCreator.getTypeElement());
Writer writer = sourceFile.openWriter();
writer.write(code);
writer.flush();
writer.close();
}
} catch (Exception e) {
e.printStackTrace();
messager.printMessage(Diagnostic.Kind.ERROR,e.getMessage());
}
messager.printMessage(Diagnostic.Kind.NOTE,"process finish....");
return false;
}
}
我们知道,使用ButterKnife会帮我们生成我们需要的辅助类的java文件,那么我们就用上面的注解处理器帮我们生成一个java文件,现在自定义个工具类(JavaCodeCreator),让代码帮我们生成指定格式的代码。
/**
* 创建日期:2020/5/30 0007
*
* @author :Gentle Wen
* describe:
*/
public class JavaCodeCreator {
private Map variableElements = new HashMap<>();
private TypeElement typeElement;
private String packageName;
public JavaCodeCreator(TypeElement typeElement, Elements elementUtils) {
this.typeElement = typeElement;
this.packageName = elementUtils.getPackageOf(typeElement).getQualifiedName().toString();
}
/**
* 创建代码
*
* @return
*/
public String createCode() {
StringBuilder stringBuilder = new StringBuilder();
//创建package com.xx.xxx;
stringBuilder.append("package ").append(packageName).append(";").append("\n");
//创建import com.xx.xx.XxActivity;
stringBuilder.append("import ").append(typeElement.getQualifiedName().toString()).append(";").append("\n");
//创建public class XxActivity_LsViewBinding{还有方法}
stringBuilder.append("public class ").append(typeElement.getSimpleName()).append("_LsViewBinding").append("{\n");
stringBuilder.append(createMethod());
stringBuilder.append("}\n");
return stringBuilder.toString();
}
/**
* 创建bind方法
* 这里帮我们创建出我们需要的方法
* @return
*/
private String createMethod() {
String activityStr = "activity";
String findViewById = "findViewById";
StringBuilder stringBuilder = new StringBuilder();
//public void bind(com.xxx.xxx.XxActivity activity){
stringBuilder.append("\t\tpublic void bind").append("(").append(typeElement.getQualifiedName()).append(" ").append(activityStr).append(")").append("{\n");
for (Integer integer : variableElements.keySet()) {
VariableElement variableElement = variableElements.get(integer);
stringBuilder.append("\t\t\t\t").append(activityStr)
.append(".").append(variableElement.getSimpleName()).append(" = ")
.append("(").append(variableElement.asType().toString()).append(")")
.append(activityStr).append(".").append(findViewById).append("(").append(integer).append(")").append(";\n");
}
stringBuilder.append("\t\t}\n");
return stringBuilder.toString();
}
public void saveVariableElement(int viewId, VariableElement variableElement) {
variableElements.put(viewId, variableElement);
}
public String getFullClassName() {
return typeElement.getQualifiedName().toString()+"_LsViewBinding";
}
public Element getTypeElement() {
return typeElement;
}
}
三、工具类
创建一个java Module (butterknife_utils),用于传入我们的activity,调用我们生成的XxActivity_LsViewBinding对象的方法
/**
* 创建日期:2020/5/30 0007
*
* @author :Gentle Wen
* describe:
*/
public class ButterKnifeManager {
public static void inject(Activity activity){
try {
//你当前activity的对象的class
Class activityClass = activity.getClass();
//根据你当前对象的路径反射找到我们生成的XxActivity_LsViewBinding路径
Class clazz = Class.forName(activityClass.getName()+"_LsViewBinding");
//反射我们的bindView方法,形参就是我们的activity
Method method = clazz.getMethod("bind", activityClass);
//带入activity参数执行我们的bindView方法
method.invoke(clazz.newInstance(),activity);
} catch (Exception e) {
e.printStackTrace();
}
}
}
四、项目中使用
1.build.gradle
defaultConfig {
javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
}
implementation project(':butterknife_utils')
implementation project(':butterknife_annotation')
annotationProcessor project(':butterknife')
2.MainActivity
package com.xx.xx;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import com.xx.butterknife_annotation.LsBindView;
import com.xx.butterknife_utils.ButterKnifeManager;
public class MainActivity extends Activity {
@LsBindView(R.id.button)
Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnifeManager.inject(this);
button.setOnClickListener(v -> {
Log.i("MainActivity", "onClick: " + "LsBindView实现");
});
}
}
3.build一下你的project,之后就会在你的build文件夹路径下生成一个MainActivity_LsViewBinding的java文件
package com.xx.xx;
import com.xx.xx.MainActivity;
public class MainActivity_LsViewBinding{
public void bind(com.xx.xx.MainActivity activity){
activity.button = (android.widget.Button)activity.findViewById(2130968578);
}
}
4.运行你的项目,点击按钮
MainActivity: onClick: LsBindView实现
总结
综上所述,我们可以知道,自定义注解处理器可以帮我们实现我们简单却又不得不实现的逻辑。在众多的框架中我们都可以看到注解处理器的影子(如:ButterKnife、Daggar等),能够掌握APT,也能在往Android架构师的路上添砖加瓦啊。