编译时注解学习三之 注解处理器AbstractProcessor
1 如何读取build.gradle配置的常量
配置,读取:
@SupportedOptions({"CLASSNAME"})
@SupportedAnnotationTypes("com.ldx.annotationlib.BindView")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
//@AutoService(Processor.class)
public class AptProcessor extends AbstractProcessor {
private Map<String,String> mOptionMap;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mOptionMap = processingEnv.getOptions();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (set != null && set.size() > 0){
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
for (Element element : elements) {
VariableElement variableElement = (VariableElement) element;
//读取配置参数
mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessor "+mOptionMap.get("CLASSNAME"));
createFile(enclosingElement, bindViewFiledClassType, bindViewFiledName, bindAnnotation.value());
}
return true;
}
return false;
}
build.gradle中配置(如何配置编译时注解变量):
javaCompileOptions {
annotationProcessorOptions {
arguments = ["xxxx":"xxxxx", "CLASSNAME":"lidxclassname"]
}
}
2 打印信息
由于在编译期,所以无法利用System,log,打印信息,系统提供了Messager工具进行信息的打印。
private Messager mMessager = processingEnv.getMessager();;
打印日志的几个方法
第一个参数Kind标识日志的级别,包括:
public static enum Kind {
ERROR,
WARNING,
MANDATORY_WARNING,
NOTE,
OTHER;
private Kind() {
}
}
第二个参数为打印信息,后面参数Element为需要打印的Element。
3 Elements 工具
Elements mElementsUtils = processingEnv.getElementUtils();
方法
//包名
mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessor "+mElementsUtils.getPackageOf(enclosingElement).asType().toString());
mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessortwo "+mElementsUtils.getBinaryName(enclosingElement));
mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessortwo "+mElementsUtils.getPackageOf(enclosingElement).getQualifiedName().toString());
mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessortwo "+mElementsUtils.isDeprecated(enclosingElement));
mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessortwo "+mElementsUtils.getPackageElement("com.ldx.canvasdrawdemo").getQualifiedName());
4 Filer,生成文件的工具类
Filer mFilerUtils = processingEnv.getFiler();
完全类名:javax.annotation.processing.Filer,注解处理器可用此创建新文件(源文件、类文件、辅助资源文件)。由此方法创建的源文件和类文件将由管理它们的工具(javac)处理
介绍 生成文件方法
apt常用方法为:
参数解释
- 第一个参数为生成的java文件的全限定名(也就是包名和文件名,写法应该是包名(com.ldx.xx)然后加上一个点(.)在加上文件名),例如要生成com.ldx.xx.demo.java,第一个参数写法为"com.ldx.xx.demo",其中demo就是文件名。
- 第二个参数是与此文件的创建有因果关联的类型或包元素,该参数可以省略或者为 null,类型为一个数组,一般写法为new Element[]{}或者为null。
//例子
private void createFile(TypeElement enclosingElement, String bindViewFiledClassType, String bindViewFiledName, int id) {
String pkName = mElementsUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
String packName = mElementsUtils.getPackageOf(enclosingElement).asType().toString();
String className = enclosingElement.getSimpleName().toString();
try {
JavaFileObject jfo = mFilerUtils.createSourceFile(pkName + "."+className+ "$ViewBinding", new Element[]{});
Writer writer = jfo.openWriter();
writer.write(brewCode(className,pkName, bindViewFiledClassType, bindViewFiledName, id));
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private String brewCode(String className,String pkName, String bindViewFiledClassType, String bindViewFiledName, int id) {
StringBuilder builder = new StringBuilder();
builder.append("package " + pkName + ";\n\n");
builder.append("public class " + className + "$ViewBinding { \n\n");
builder.append("public static void main(String[] args){ \n\n");
String info = String.format("%s %s = findViewById(%d)", bindViewFiledClassType, bindViewFiledName, id);
builder.append("System.out.println(\"" + info + "\");\n\n");
builder.append("}\n\n");
builder.append("}");
return builder.toString();
}
5 ElementKind
如何判断Element的类型呢,需要用到ElementKind,ElementKind为元素的类型,元素的类型判断不需要用instanceof去判断,而应该通过getKind()去判断对应的类型
类型 说明
PACKAGE 包
ENUM 枚举
CLASS 类
ANNOTATION_TYPE 注解
INTERFACE 接口
ENUM_CONSTANT 枚举常量
FIELD 字段
PARAMETER 方法参数
LOCAL_VARIABLE 局部变量
METHOD 方法
CONSTRUCTOR 构造方法
TYPE_PARAMETER 类型参数
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
for (Element element : elements) {
ElementKind kind = element.getKind();
if (kind.isField()){
mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessor "+"element 是 Field,利用VariableElement进行强转");
}
}
6 TypeMirror
TypeMirror是一个接口,表示Java编程语言中的类型。这些类型包括基本类型、引用类型、数组类型、类型变量和null类型等等
类型 说明
ArrayType 表示数组类型
DeclaredType 表示声明类型(类或接口类型)
ErrorType 表示异常类或接口类型
ExecutableType 表示executable类型(方法、构造方法、初始化)
NoType 表示在实际类型不适合的地方使用的伪类型
NullType 表示null类型
PrimitiveType 表示基本数据类型
ReferenceType 表示引用类型
TypeVariable 表示类型变量
WildcardType 表示通配符类型参数
for (Element element : elements) {
ElementKind kind = element.getKind();
if (kind.isField()){
mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessor "+"element 是 Field,利用VariableElement进行强转");
TypeMirror mirror = element.asType();
TypeKind kind1 = mirror.getKind();
}
如果Element是一个VariableElement,variableElement.asType().toString()可以获取它的全类名。
String bindViewFiledClassType = variableElement.asType().toString();
//变量名
mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessor "+bindViewFiledClassType);
输出:
android.widget.TextView
7 TypeKind
TypeKind为类型的属性,类型的属性判断不需要用instanceof去判断,而应该通过getKind()去判断对应的属性
类型 说明
BOOLEAN 基本类型boolean
INT 基本类型int
LONG 基本类型long
FLOAT 基本类型float
DOUBLE 基本类型double
VOID 对应于关键字void的伪类型
NULL null类型
ARRAY 数组类型
PACKAGE 对应于包元素的伪类型
EXECUTABLE 方法、构造方法、初始化
DECLARE 声明类型
for (Element element : elements) {
ElementKind kind = element.getKind();
if (kind.isField()){
mMessager.printMessage(Diagnostic.Kind.NOTE,"aptprocessor "+"element 是 Field,利用VariableElement进行强转");
TypeMirror mirror = element.asType();
TypeKind kind1 = mirror.getKind();
}
8 Types
Types mTypesUtils = processingEnv.getTypeUtils();
Type操作工具,包括对TypeMirror,TypeKind,DeclaredType
方法1 方法2
asElement,根据特定TypeMirror获取与之关联的ElementType。
contains,是否一个TypeMirror包含另外一个TypeMirror,类似数组和普通元素。
。。。。。。。