APT的学习要花点时间去掌握和实践的,短时间内只能掌握知识点,更多的是在实战中去实践。其实,APT就是一种工具而已,只要用多了,自然就会熟练了,不过要想实践之前,还是必须把基础知识学好才能实战进入开发。文章会从基础用例讲解知识点,然后再通过实战进行实践
APT(Annotation Processing Tool)是一种处理注解的工具,它会对源代码中的注解进行额外的处理,比如在编译时生成一些重复性操作的Java代码,或者不需要程序员去关心的Java代码等。在使用APT的过程中会涉及到下面两个第三方库的使用
APT主要过程包括初始化过程和注解处理过程
1、定义注解
该注解是可以在我们的项目中使用到的,且规定为注解元素的类型为Type,和在编译时生效
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ModuleWrapper {
}
2、定义Processor
Processor会在编译期对注解进行解析,取出对应的元素进行处理。至于AutoService则是固定的写法,加个注解即可
@AutoService(Processor.class)
public class ModuleProcessor extends AbstractProcessor {
private Filer filerUtils; // 文件写入
private Elements elementUtils; // 操作Element工具类
private Messager messagerUtils; // Log 日志
private Map options; // 额外配置参数
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
filerUtils = processingEnvironment.getFiler();
elementUtils = processingEnvironment.getElementUtils();
messagerUtils = processingEnvironment.getMessager();
options = processingEnvironment.getOptions();
}
@Override
public Set getSupportedAnnotationTypes() {
Set types = new LinkedHashSet<>();
types.add(ModuleWrapper.class.getCanonicalName());
return types;
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
initModuleMap(roundEnvironment);
return false;
}
private void initModuleMap(RoundEnvironment roundEnv) {
//获取对应的注解元素
Set extends Element> set = roundEnv.getElementsAnnotatedWith(ModuleWrapper.class);
for (Element element : set) {
//如果是个类
if (element.getKind() == ElementKind.CLASS) {
//获取类名
String clzName = element.getSimpleName().toString();
//对元素进行处理,可用javapoet生成Java代码
......
} else {
messagerUtils.printMessage(Diagnostic.Kind.NOTE, "only support class");
}
}
}
}
3、AbstractProcessor实现方法介绍
1、初始化介绍
APT初始化阶段为init
方法的调用,我们可以使用ProcessingEnvironment
获取一些实用类以及获取选项参数等
方法 | 说明 |
---|---|
getElementUtils() | 返回实现Elements接口的对象,用于操作元素的工具类 |
getFiler() | 返回实现Filer接口的对象,用于创建文件、类和辅助文件 |
getMessager() | 返回实现Messager接口的对象,用于报告错误信息、警告提醒 |
getOptions() | 返回指定的参数选项 |
getTypeUtils() | 返回实现Types接口的对象,用于操作类型的工具类 |
2、Element介绍
Element是操作元素最主要的类,可通过getElementsAnnotatedWith
获取Element的元素,经过getKind()
判断元素的类型后,可强制转换成对应的类型,在对应的类型中有着不同的方法可以调用
类型 | 说明 |
---|---|
ExecutableElement | 表示类、接口的方法元素。包括构造方法、注解类型 |
PackageElement | 表示包元素。提供对有关包及其成员的信息的访问 |
TypeElement | 表示类、接口元素。提供对有关类型及其成员的信息的访问 |
TypeParameterElement | 表示类、接口、方法、构造方法的参数元素 |
VariableElement | 表示字段、enum、方法、构造方法参数、局部变量、异常参数 |
ElementKind为元素的类型,元素的类型判断不需要用instanceof
去判断,而应该通过getKind()
去判断对应的类型
类型 | 说明 |
---|---|
PACKAGE | 包 |
ENUM | 枚举 |
CLASS | 类 |
ANNOTATION_TYPE | 注解 |
INTERFACE | 接口 |
ENUM_CONSTANT | 枚举常量 |
FIELD | 字段 |
PARAMETER | 方法参数 |
LOCAL_VARIABLE | 局部变量 |
METHOD | 方法 |
CONSTRUCTOR | 构造方法 |
TYPE_PARAMETER | 类型参数 |
3、TypeMirror介绍
TypeMirror是一个接口,表示Java编程语言中的类型。这些类型包括基本类型、引用类型、数组类型、类型变量和null类型等等
类型 | 说明 |
---|---|
ArrayType | 表示数组类型 |
DeclaredType | 表示声明类型(类或接口类型) |
ErrorType | 表示异常类或接口类型 |
ExecutableType | 表示executable类型(方法、构造方法、初始化) |
NoType | 表示在实际类型不适合的地方使用的伪类型 |
NullType | 表示null类型 |
PrimitiveType | 表示基本数据类型 |
ReferenceType | 表示引用类型 |
TypeVariable | 表示类型变量 |
WildcardType | 表示通配符类型参数 |
TypeKind为类型的属性,类型的属性判断不需要用instanceof
去判断,而应该通过getKind()
去判断对应的属性
类型 | 说明 |
---|---|
BOOLEAN | 基本类型boolean |
INT | 基本类型int |
LONG | 基本类型long |
FLOAT | 基本类型float |
DOUBLE | 基本类型double |
VOID | 对应于关键字void的伪类型 |
NULL | null类型 |
ARRAY | 数组类型 |
PACKAGE | 对应于包元素的伪类型 |
EXECUTABLE | 方法、构造方法、初始化 |
这里需要注意的是,如果我们通过注解去获取Class类型的值,如果获取的Class未被编译,则会抛出MirroredTypeException异常,此时我们需要通过try-catch语句在catch里去获取我们所需要的类元素
try {
annotation.value();//如果value为Class类型则会报异常
} catch (MirroredTypeException mte) {
DeclaredType classTypeMirror = (DeclaredType) mte.getTypeMirror();
TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement();//通过异常去获取类元素
}
4、Filer介绍
Filer接口支持通过注解处理器创建新文件。可以创建三种文件类型:源文件、类文件和辅助资源文件
方法 | 说明 |
---|---|
createSourceFile | 创建源文件 |
createClassFile | 创建类文件 |
createResource | 创建辅助资源文件 |
5、Messager介绍
Messager接口提供注解处理器用来报告错误消息、警告和其他通知的方式
方法 | 说明 |
---|---|
printMessage | 打印错误消息 |
6、Options介绍
通过getOptions()方法获取选项参数,在gradle文件中配置选项参数值
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [ version : '1.0.0' ]
}
}
}
}
通过ProcessingEnvironment
去获取对应的参数
processingEnvironment.getOptions().get("version");
7、获取注解元素
通过RoundEnvironment
接口去获取注解元素,通过JavaPoet生成Java代码
方法 | 说明 |
---|---|
getElementsAnnotatedWith | 返回注解元素的集合 |
下面通过APT的实战,进行对项目的模块化划分
1、项目结构
注意事项:创建Module的时候,需要选择java Lib,而不是Android Lib
2、Gradle配置
annotation的Module必须声明Java编译版本
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
compiler的Module必须声明Java编译版本,且依赖于annotation和导入我们所需的库
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':annotation')
implementation 'com.google.auto.service:auto-service:1.0-rc2'
implementation 'com.squareup:javapoet:1.7.0'
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
主工程必须通过依赖annotaion和compiler,由于我们只是在编译期生效,可用annotationProcessor
implementation project(':annotation')
annotationProcessor project(':compiler')
注意事项:定义编译的jdk版本为1.7
3、定义注解
ModuleWrapper注解,表示需要加载的模块
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ModuleWrapper {
}
IModule接口,表示当前类是一个模块类
public interface IModule {
String getModuleName();
}
4、定义Processor
@AutoService(Processor.class)
public class ModuleProcessor extends AbstractProcessor {
private Map moduleMaps = new HashMap<>();
private Filer filerUtils; // 文件写入
private Elements elementUtils; // 操作Element 的工具类
private Messager messagerUtils; // Log 日志
private Map options;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
filerUtils = processingEnvironment.getFiler();
elementUtils = processingEnvironment.getElementUtils();
messagerUtils = processingEnvironment.getMessager();
options = processingEnvironment.getOptions();
}
@Override
public Set getSupportedAnnotationTypes() {
Set types = new LinkedHashSet<>();
types.add(ModuleWrapper.class.getCanonicalName());
return types;
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
try {
initModuleMap(roundEnvironment);
createModuleMap();
createModuleConstant();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* 通过注解元素获取组件实体
*
* @param roundEnv
*/
private void initModuleMap(RoundEnvironment roundEnv) {
Set extends Element> set = roundEnv.getElementsAnnotatedWith(ModuleWrapper.class);
for (Element element : set) {
if (element.getKind() == ElementKind.CLASS) {
String clzName = element.getSimpleName().toString();
if (moduleMaps.get(clzName) == null) {
ModuleInfo info = new ModuleInfo(elementUtils, (TypeElement) element);
moduleMaps.put(clzName, info);
}
} else {
messagerUtils.printMessage(Diagnostic.Kind.NOTE, "only support class");
}
}
}
/**
* 创建组件管理者
*
* @throws IOException
*/
private void createModuleMap() throws IOException {
FieldSpec fieldSpec = FieldSpec
.builder(ParameterizedTypeName.get(HashMap.class, String.class, IModule.class)
, "moduleMap", Modifier.PRIVATE)
.initializer("new HashMap<>()")
.build();
CodeBlock.Builder codeBlock = CodeBlock.builder();
for (String key : moduleMaps.keySet()) {
ModuleInfo info = moduleMaps.get(key);
codeBlock.addStatement("moduleMap.put($S ,new $T())", info.getFullClassName(),
ClassName.get(info.packageName, info.className));
}
MethodSpec initMethod = MethodSpec.methodBuilder("init")
.addModifiers(Modifier.PUBLIC)
.addCode(codeBlock.build())
.returns(TypeName.VOID)
.build();
MethodSpec getMethod = MethodSpec.methodBuilder("get")
.addModifiers(Modifier.PUBLIC)
.addParameter(String.class, "cls")
.addStatement("return moduleMap.get(cls)")
.returns(IModule.class)
.build();
ArrayList methods = new ArrayList<>();
methods.add(initMethod);
methods.add(getMethod);
TypeSpec moduleFactory = TypeSpec.classBuilder("ModuleFactory")
.addModifiers(Modifier.PUBLIC)
.addMethods(methods)
.addField(fieldSpec)
.build();
JavaFile javaFile = JavaFile.builder("com.hensen.compiler.processor", moduleFactory)
.build();
javaFile.writeTo(filerUtils);
}
/**
* 创建组件常量
*
* @throws IOException
*/
private void createModuleConstant() throws IOException {
TypeSpec.Builder moduleConstant = TypeSpec.classBuilder("ModuleConstant")
.addModifiers(Modifier.PUBLIC);
for (String key : moduleMaps.keySet()) {
ModuleInfo info = moduleMaps.get(key);
FieldSpec fieldSpec = FieldSpec.builder(String.class, info.className)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer("$S", info.getFullClassName())
.build();
moduleConstant.addField(fieldSpec);
}
JavaFile javaFile = JavaFile.builder("com.hensen.compiler.processor", moduleConstant.build())
.build();
javaFile.writeTo(filerUtils);
}
}
5、组件实体
组件实体保存着组件的信息
public class ModuleInfo {
public String packageName;
public String className;
public ModuleInfo(Elements elementUtils, TypeElement typeElement) {
packageName = getPackageName(elementUtils, typeElement);
className = getClassName(typeElement, packageName);
}
public String getClassName(TypeElement type, String packageName) {
int packageLen = packageName.length() + 1;
return type.getQualifiedName().toString().substring(packageLen)
.replace('.', '$');
}
public String getPackageName(Elements elementUtils, TypeElement classElement) {
PackageElement packageElement = elementUtils.getPackageOf(classElement);
return packageElement.getQualifiedName().toString();
}
public String getFullClassName() {
return packageName + "." + className;
}
public String getClassName(){
return className;
}
}
6、注解使用
分别创建出礼物模块和聊天模块,聊天模块增加发消息的方法
@ModuleWrapper
public class ChatModule implements IModule{
@Override
public String getModuleName() {
return "ChatModule";
}
public void sendMessage() {
Log.i("TAG", "Hi");
}
}
@ModuleWrapper
public class GiftModule implements IModule{
@Override
public String getModuleName() {
return "GiftModule";
}
}
7、生成代码
在生成代码之前需要gradle build,查看生成代码。当然对于模块来说,不仅有get()、还有add()、remove()等其他扩展功能,具体的就留给大家去操作
public class ModuleFactory {
private HashMap moduleMap = new HashMap<>();
public void init() {
moduleMap.put("com.hensen.geneapt.GiftModule" ,new GiftModule());
moduleMap.put("com.hensen.geneapt.ChatModule" ,new ChatModule());
}
public IModule get(String cls) {
return moduleMap.get(cls);
}
}
public class ModuleConstant {
public static final String GiftModule = "com.hensen.geneapt.GiftModule";
public static final String ChatModule = "com.hensen.geneapt.ChatModule";
}
8、组件使用
初始化组件的加载,通过工厂获取对应的模块进行操作
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ModuleFactory moduleFactory = new ModuleFactory();
moduleFactory.init();
ChatModule chatModule = (ChatModule) moduleFactory.get(ModuleConstant.ChatModule);
chatModule.sendMessage();
}
}
9、源码下载
源码下载