Android基础知识-注解工具APT

转载

APT技术功能

APT(Annotation Processing Tool) 是 javac 提供 的一种 编译时扫描、处理注解的工具
它会对源代码文件进行检查,并找出其中的注解,然后根据用户自定义的注解处理方法进行额外的处理。

APT 工具能 解析注解,根据注解生成其他的源文件,将生成的新的源文件、原来的源文件【共同编译】
(注意:APT 不能对源文件进行修改操作,只能生成新的文件,例如在已有的类中添加方法)

注解处理器是 运行 它自己的虚拟机JVM
javac 启动一个 完整Java虚拟机 来运行注解处理器

2、APT使用依赖

1)注解处理器库(包含我们的注解处理器)
Android项目默认是不包含 APT相关类的。要使用APT技术,必须创建一个Java Library

2)注解声明库(用于存储声明的注解)

开发者并不希望已经编译好的项目中,包含处理器相关代码,他仅仅想使用我们的注解。
把注解处理器 与 注解 分开单独抽成一个库是有必要的

3)实际使用 APT的 Android/Java项目

3、注解处理器的声明

继承 AbstractProcessor 类

class MineProcessor extends AbstractProcessor {
	/*
	* 注解处理器被初始化的时候都会被调用
	* 参数 ProcessingEnvironment 能提供很多有用的工具类,Elements、Types、Filer
	*/
	@Override
	public synchronized void init(ProcessingEnvironment processingEnv) {}
	
	/*
	* 注解处理器实际处理方法,一般要求子类实现该抽象方法,
	* 可以在这里写扫描与处理注解的代码,以及生成Java文件。
	*
	* 其中参数RoundEnvironment ,可以查询出包含特定注解的被注解元素
	*/
	@Override
	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
		return false;
	}
	
	/*
	* 用来指定你使用的Java版本,
	* 通常返回 SourceVersion.latestSupported()。
	*
	* 或者 SourceVersion.RELAEASE_XX 指定具体版本。
	*/
	@Override
	public SourceVersion getSupportedSourceVersion() { }
	
	/*
	* 返回当前注解处理器处理注解的类型
	* 
	* 返回值为一个字符串的集合。其中字符串为处理器需要处理的注解的合法全称。
	*/
	@Override
	public Set<String> getSupportedAnnotationTypes() { }
}

在【Java1.6 版本】中提供了 @SupportedAnnotationTypes、@SupportedSourceVersion 两个注解
来替代getSupportedSourceVersion与getSupportedAnnotationTypes两个方法。

@SupportedSourceVersion(SourceVersion.RELEASE_6)
@SupportedAnnotationTypes({"合法注解的名称"})
class MineProcessor extends AbstractProcessor {

	@Override
	public synchronized void init(ProcessingEnvironment processingEnv) {
	}

	@Override
	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
		return false;
	}	
}
4、注册注解处理器

1)目录结构

-src
	-main
		-java
			-processor
				MineProcessor
		-resources
			-META-INF.services
				javax.annotation.processing.Processor

META-INF/services 相当于一个信息包,
目录中的文件、目录获得 Java平台的认可 与 解释 用来配置应用程序、扩展程序、类加载器和服务文件,
在jar打包时自动生成。

javax.annotation.processing.Processor 文件中的内容:
每个注解处理器的合法的全名列表,每一个元素换行分割

javax.annotation.processing.Processor 文件中的内容为【每个注解处理器的合法的全名列表】
每一个元素换行分割,如下:
	com.jennifer.andy.processor.MineProcessor1
	com.jennifer.andy.processor.MineProcessor2
	com.jennifer.andy.processor.MineProcessor3

将你生成的.jar放到你的buildPath中,
那么Java编译器会自动的检查和读取javax.annotation.processing.Processor中的内容,
并注册该注解处理器。

2)Android Studio中使用

只要创建1)中文件夹和文件,不用把jar放到buildPath,IDE会帮我们处理。
或者,使用 Google 提供的 AutoService 注解处理器,用于完成1)中创建目录和文件的工作。

@SupportedSourceVersion(SourceVersion.RELEASE_6)
@SupportedAnnotationTypes({"要处理的注解的名称"})
@AutoService(Processor.class)
class MineProcessor extends AbstractProcessor {

	@Override
	public synchronized void init(ProcessingEnvironment processingEnv) {
	}

	@Override
	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
		return false;
	}
}

使用 AutoService,需要在Gradle 中添加依赖:
compile 'com.google.auto.service:auto-service:1.0-rc2’

4、注解处理器 的扫描

注解处理过程中,会 扫描所有的Java源文件,源代码的每一个部分都是一个特定类型的 Element
Element 代表源文件中的元素,例如包、类、字段、方法等。

1)Element子/孙类:

Parameterizable:表示 混合类型的元素(不仅只有一种类型的Element)
TypeParameterElement带有泛型参数 的类、接口、方法、构造器
VariableElement:表示 字段、常量、方法、构造函数。参数、局部变量、资源变量、异常参数
QualifiedNameable具有限定名称的元素
ExecutableElement:表示 类、接口的方法、构造函数、初始化器(静态或实例),包括注释类型元素。
TypeElement :表示 类和接口
PackageElement:表示

例子:

package com.jennifer.andy.aptdemo.domain;//PackageElement
class Person {//TypeElement 
	private String where;//VariableElement
	
	public void doSomething() { }//ExecutableElement
	
	public void run() {//ExecutableElement
		int runTime;//VariableElement
	}
}
	
获取一个元素的父元素和子元素:
TypeElement person= ... ;  
for (Element e : person.getEnclosedElements()){ // 遍历它的孩子 
	Element parent = e.getEnclosingElement();  //拿到孩子元素的最近的父元素
}

2)元素种类判断

例子:

public class SpiltElementProcessor extends AbstractProcessor {
	@Override
	public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
		//获取源文件中所有包含@Who注解的元素
		Set<? extends Element> elements = env.getElementsAnnotatedWith(Who.class);
		for (Element element : elements) {
			//element.getKind()具体判断当前元素种类
			if (element.getKind() == ElementKind.CLASS) {//如果元素是类

			} else if (element.getKind() == ElementKind.INTERFACE) {//如果当前元素是接口

			}
		}
		return false;
	}
	...
}

ElementKind枚举:

public enum ElementKind {
	PACKAGE, 				//包
	ENUM,					//枚举
	CLASS,					//类
	ANNOTATION_TYPE,		//注解
	INTERFACE,				//接口
	ENUM_CONSTANT,			//枚举常量
	FIELD,					//字段
	PARAMETER,				//参数
	LOCAL_VARIABLE,			//本地变量
	EXCEPTION_PARAMETER,	//异常参数
	METHOD,					//方法
	CONSTRUCTOR,			//构造函数
	STATIC_INIT,			//
	INSTANCE_INIT,
	TYPE_PARAMETER,
	OTHER,
	RESOURCE_VARIABLE;
	...
}

3)元素类型判断
通过 TypeMirror 来获取元素类型

public class TypeKindSpiltProcessor extends AbstractProcessor {
	@Override
	public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
		Set<? extends Element> elements = env.getElementsAnnotatedWith(Who.class);
		for (Element element : elements) {
			if (element.getKind() == ElementKind.METHOD) {//如果当前元素是方法
				ExecutableElement methodElement = (ExecutableElement) element;
				//获取TypeMirror
				TypeMirror returnType = methodElement.getReturnType();
				//获取元素类型
				TypeKind kind = returnType.getKind();
				System.out.println("print return type----->" + kind.toString());
			}
		}
		return false;
	}
}

TypeKind 枚举类型:

public enum TypeKind {
	BOOLEAN, 		//boolean 类型
	BYTE,			//byte 类型
	SHORT,			//short 类型
	INT,			//int 类型
	LONG,			//long 类型
	CHAR,			//char 类型
	FLOAT,			//float 类型
	DOUBLE,			//double 类型
	VOID,			//void 类型,主要用于方法的返回值
	NONE,			//无类型
	NULL,			//空类型
	ARRAY,			//数组类型
	DECLARED,
	ERROR,
	TYPEVAR,
	WILDCARD,
	PACKAGE,
	EXECUTABLE,
	OTHER,
	UNION,
	INTERSECTION;
	...
}

3)元素可见性修饰符
在注解处理器中,我们不仅能获得元素的种类和信息,
我们还能获取该元素的 可见性修饰符(例如public、private等)
我们可以直接调用 Element.getModifiers()

public class GetModifiersProcessor extends AbstractProcessor {

	@Override
	public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
		Set<? extends Element> elements = env.getElementsAnnotatedWith(Who.class);
		for (Element element : elements) {
			if (element.getKind() == ElementKind.CLASS) {//如果元素是类
				Set<Modifier> modifiers = element.getModifiers();//获取可见性修饰符
				if (!modifiers.contains(Modifier.PUBLIC)) {//如果当前类不是public
					throw new ProcessingException(classElement, "The class %s is not public.",
							classElement.getQualifiedName().toString());
				}
			}
		return false;
	}
}

Modifer 为枚举类型:

public enum Modifier {
	PUBLIC,
	PROTECTED,	
	PRIVATE,
	ABSTRACT,
	DEFAULT,
	STATIC,
	FINAL,
	TRANSIENT,
	VOLATILE,
	SYNCHRONIZED,
	NATIVE,
	STRICTFP;
	...
}

4)错误处理 Messager

env.getMessager().printMessage(Diagnostic.Kind.NOTE, msg)
用来写一些信息给使用此注解库的第三方开发者

Diagnostic.Kind 消息级别枚举

public class GetModifiersProcessor extends AbstractProcessor {

	@Override
	public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
		Set<? extends Element> elements = env.getElementsAnnotatedWith(Who.class);
		for (Element element : elements) {
			if (element.getKind() == ElementKind.CLASS) {//如果元素是类
				Set<Modifier> modifiers = element.getModifiers();//获取可见性修饰符
				if (!modifiers.contains(Modifier.PUBLIC)) {//如果当前类不是public
					/* 用来写一些信息给使用此注解库的第三方开发者的
					 * env.getMessager().printMessage(Diagnostic.Kind.NOTE, msg)
					 */
					env.getMessager().printMessage(Diagnostic.Kind.ERROR, "the class is not public");
				}
			}
		return false;
	}
}
4、生成新的类(也就是创建新的源文件)

通过 JavaPoet 来构造源文件

在gradle中你需要添加依赖:
compile 'com.google.auto.service:auto-service:1.0-rc2’

1)JavaPoet 的使用

@AutoService(Processor.class)
@SupportedAnnotationTypes("com.jennifer.andy.apt.annotation.Who")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class CreateFileByJavaPoetProcessor extends AbstractProcessor {

	@Override
	public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
		createFileByJavaPoet(set, env);
		return false;
	}
	
	/**
	 * 通过JavaPoet生成新的源文件
	 */
	private void createFileByJavaPoet(Set<? extends TypeElement> annotations, RoundEnvironment env) {
		//创建 main 方法
		MethodSpec main = MethodSpec.methodBuilder("main")
				//设置可见性修饰符public static
				.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
				//设置返回值为void
				.returns(void.class)
				//添加参数类型为String数组,且参数名称为args
				.addParameter(String[].class, "args")
				//添加语句
				.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
				.build();
		//创建类
		TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
				.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
				//将main方法添加到HelloWord类中
				.addMethod(main)
				.build();

		//创建文件,第一个参数是包名,第二个参数是相关类
		JavaFile javaFile = JavaFile.builder("com.jennifer.andy.aptdemo.domain", helloWorld)
				.build();
		try {
			//创建文件
			javaFile.writeTo(processingEnv.getFiler());
		} catch (IOException e) {
			log(e.getMessage());
		}
	}

	/**
	 * 调用打印语句而已
	 */
	private void log(String msg) {
		processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, msg);
	}
}
5、分离处理器和项目

annotationProcessor 是APT工具中的一种
它是google开发的内置框架,不需要引入,可以直接在build.gradle文件中使用。

使用 annotationProcessor:
1)允许 只将编译时注解处理器配置为依赖项,而不在最终apk 或库中包括工件
2)设置源路径,以便 Android Studio 能正确找到注解处理器生成的代码

使用:

//如果是本地库
annotationProcessor project(':apt_compiler')
//如果是远程库
annotationProcessor 'com.jakewharton:butterknife-compiler:9.0.0-rc1'

转载自:
Android 注解系列之APT工具(三)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值