APT可以根据注解,在编译时生成代码。

1.    创建两个java library

2.    依赖

factory-compiler的依赖

dependencies {implementation 'com.google.auto.service:auto-service:1.0-rc4'implementation project(':annotation')implementation 'com.squareup:javapoet:1.10.0'annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
}

app的

implementation project(':annotation')
annotationProcessor project(':factory-compiler')

3.    创建注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Factory {Class type();String id();}

4.    注解使用

在app中创建接口,还有对应实现类

public interface IShape {void show();
}@Factory(type = IShape.class, id = "Circle")
public class Circle implements IShape {@Overridepublic void show() {System.out.println("Circle");}
}@Factory(type = IShape.class, id = "Rectangle")
public class Rectangle implements IShape {@Overridepublic void show() {System.out.println("Rectangle");}
}

在MainActivity中使用

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);IShapeFactory iShapeFactory = new IShapeFactory();IShape circle = iShapeFactory.create("Circle");TextView tv = findViewById(R.id.tv);tv.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {circle.show();}});}@Overrideprotected void onResume() {super.onResume();}
}

此时我们还没有IShapeFactory这个类,需要用APT在编译生成。

5.    实现APT

首先我们创建两个实体类

public class FactoryAnnotatedClass {private TypeElement typeElement;private String canonicalName;private String simpleTypeName;private final String id;public FactoryAnnotatedClass(TypeElement classElement) {this.typeElement = classElement;Factory annotation = classElement.getAnnotation(Factory.class);id = annotation.id();try {  // 该类已经被编译Class<?> clazz = annotation.type();canonicalName = clazz.getCanonicalName();simpleTypeName = clazz.getSimpleName();} catch (MirroredTypeException mte) {// 该类未被编译DeclaredType classTypeMirror = (DeclaredType) mte.getTypeMirror();TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement();canonicalName = classTypeElement.getQualifiedName().toString();simpleTypeName = classTypeElement.getSimpleName().toString();}}……get方法
}
public class FactoryGroup {private static final String SUFFIX = "Factory";public final String qualifiedClassName;private final Map<String, FactoryAnnotatedClass> itemsMap = new LinkedHashMap<>();public FactoryGroup(String qualifiedClassName) {this.qualifiedClassName = qualifiedClassName;}public void add(FactoryAnnotatedClass toInsert) {itemsMap.put(toInsert.getId(), toInsert);}public void generateCode(Elements elementUtils, Filer filer) {TypeElement superClassName = elementUtils.getTypeElement(qualifiedClassName);String factoryClassName = superClassName.getSimpleName() + SUFFIX;PackageElement pkg = elementUtils.getPackageOf(superClassName);String packageName = pkg.isUnnamed() ? null : pkg.getQualifiedName().toString();MethodSpec.Builder method = MethodSpec.methodBuilder("create").addModifiers(Modifier.PUBLIC).addParameter(String.class, "id").returns(TypeName.get(superClassName.asType()));method.beginControlFlow("if (id == null)").addStatement("throw new IllegalArgumentException($S)", "id is null!").endControlFlow();for (FactoryAnnotatedClass item : itemsMap.values()) {method.beginControlFlow("if ($S.equals(id))", item.getId()).addStatement("return new $L()", item.getTypeElement().getQualifiedName().toString()).endControlFlow();System.out.println("QualifiedName      "+item.getTypeElement().getQualifiedName().toString());}method.addStatement("throw new IllegalArgumentException($S + id)", "Unknown id = ");TypeSpec typeSpec = TypeSpec.classBuilder(factoryClassName).addModifiers(Modifier.PUBLIC).addMethod(method.build()).build();try {if (packageName != null) {JavaFile.builder(packageName, typeSpec).build().writeTo(filer);}} catch (IOException e) {e.printStackTrace();}}
}

最主要的就是generateCode方法,里面利用了JavaPoet生成代码。

接下来创建一个类继承AbstractProcessor,主要实现下面两个方法。

@AutoService(Processor.class)
public class FactoryProcessor extends AbstractProcessor{private final Map<String, FactoryGroup> factoryClasses = new LinkedHashMap<>();@Overridepublic Set<String> getSupportedAnnotationTypes() {Set<String> annotations = new LinkedHashSet<>();annotations.add(Factory.class.getCanonicalName());return annotations;}@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {for (TypeElement element:set){//  通过RoundEnvironment获取到所有被@Factory注解的对象Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(element);for (Element annotatedElement : elements) {TypeElement typeElement = (TypeElement) annotatedElement;FactoryAnnotatedClass annotatedClass = new FactoryAnnotatedClass(typeElement);FactoryGroup factoryGroup = factoryClasses.get(annotatedClass.getCanonicalName());if (factoryGroup == null) {String qualifiedGroupName = annotatedClass.getCanonicalName();factoryGroup = new FactoryGroup(qualifiedGroupName);factoryClasses.put(qualifiedGroupName, factoryGroup);}factoryGroup.add(annotatedClass);}for (FactoryGroup factoryGroup : factoryClasses.values()) {factoryGroup.generateCode(processingEnv.getElementUtils(), processingEnv.getFiler());}return true;}return false;}
}

process方法会被多次调用,方法参数中的set是我们注解类所对应的TypeElement对象,仅当set不为空时,我们才去操作。

然后调用RoundEnvironment的getElementsAnnotatedWith方法获取到使用了改注解的类的TypeElement对象(既Circle、Rectangle)。

然后我们把这两个TypeElement对象封装成FactoryAnnotatedClass对象,存放到factoryGroup中,简单说factoryGroup就是用来存放使用了同一个注解类的对象,为了方便我们创建多个注解一起使用所以又创建了一个map对象(既factoryClasses)来存放factoryGroup,然后我们调用generateCode方法生成对应java文件。

在generateCode方法中,我们通过获取Circle、Rectangle类注解的id,来生成代码,rebuild之后即可看到对应文件。

优点:

1、当我们需要再创建一个实现类时,只需要给实现类标上注解就可以,不想要去添加工厂类的代码。如果是自己编写的工厂类则需要再加一段代码。

2、APT>反射,解析注解可以不需要用反射的方式,APT在调式期间就可以解析。

JavaPoet使用文档: https://github.com/aheckl/javapoet

Android—APT实践相关推荐

  1. Xamarin.Android开发实践(十七)

    Xamarin.Android开发实践(十七) 原文:Xamarin.Android开发实践(十七) Xamarin.Android之定位 一.前言 打开我们手中的应用,可以发现越来越多的应用使用了定 ...

  2. Android Apt失效:找不到Apt生成的对应类

    Android Apt失效:找不到Apt生成的对应类 参考自 Android Gradle由4.x升级至5.0导致Apt项目失效 高级开发必须理解的Java中SPI机制 - 简书 Upgrading ...

  3. 【Android APT】注解处理器 ( 根据注解生成 Java 代码 )

    文章目录 一.生成 Java 代码 二.实现 IButterKnife 接口 三.视图绑定主要操作 四.完整注解处理器代码 五.博客资源 Android APT 学习进阶路径 : 推荐按照顺序阅读 , ...

  4. 【Android APT】注解处理器 ( Element 注解节点相关操作 )

    文章目录 一.获取被 注解 标注的节点 二.Element 注解节点类型 三.VariableElement 注解节点相关操作 四.注解处理器 完整代码示例 五.博客资源 Android APT 学习 ...

  5. 【Android APT】注解处理器 ( 配置注解依赖、支持的注解类型、Java 版本支持 )

    文章目录 一.注解处理器 依赖 编译时注解 二.设置 注解处理器 支持的注解类型 三.设置 注解处理器 支持的 Java 版本 四.博客资源 Android APT 学习进阶路径 : 推荐按照顺序阅读 ...

  6. 【Android APT】注解处理器 ( 注解标注 与 初始化方法 )

    文章目录 一.注解处理器 AbstractProcessor 二.使用注解 @AutoService(Processor.class) 标注 注解处理器 三.注解处理器 init 初始化方法 四.注解 ...

  7. 【Android APT】编译时技术 ( 开发编译时注解 )

    文章目录 一.编译时注解 二.编译时注解 使用 三.注解的保留时间 四.博客资源 一.编译时注解 上一篇博客 [Android APT]编译时技术 ( 编译时注解 和 注解处理器 依赖库 ) 中创建并 ...

  8. 【Android APT】编译时技术 ( 编译时注解 和 注解处理器 依赖库 )

    文章目录 一.编译时注解和注解处理器 二.创建 编译时注解 和 注解处理器 三.添加 编译时注解 和 注解处理器 依赖库依赖 四.博客资源 一.编译时注解和注解处理器 上一篇博客 [Android A ...

  9. Android最佳性能实践(二)——分析内存的使用情况

    由于Android是为移动设备开发的操作系统,我们在开发应用程序的时候应当始终把内存问题充分考虑在内.虽然Android系统拥有垃圾自动回收机制,但这并不意味着我们就可以完全忽略何时去分配或释放内存. ...

最新文章

  1. AI独角兽面对BAT,挑战还是臣服?| 《财经》封面
  2. mvvm command的使用案例
  3. 多元统计分析-判别分析
  4. linux 下如何升级CMAKE?(安装指定版本cmake)(高版本cmake)(不删除之前的,可以用软连接)
  5. PyQt4基本布局常用方法之addSpacing
  6. 03_MyBatis基本查询,mapper文件的定义,测试代码的编写,resultMap配置返回值,sql片段配置,select标签标签中的内容介绍,配置使用二级缓存,使用别名的数据类型,条件查询ma
  7. C#如何操作另一个窗体:[2]子窗体操作主窗体(转)
  8. SPI和RAM IP核
  9. 【转】DCMTK 开源库的学习笔记2:直接操作dcm文件中像素数据的尝试
  10. asp.NET去掉form的runat=server照样使用服务器控件,包括表单的服务器控件
  11. 唱吧DevOps的落地,微服务CI/CD的范本技术解读
  12. Android安全:Hook技术
  13. 7万硕士、21万本科生在送外卖,是自愿还是工作难找无奈之举?
  14. 深入分析HDFS原理及读写流程
  15. 计算机四级网络工程师(计算机网络多选)- 知识点
  16. 电子电路之电阻篇01——贴片电阻01.常用阻值及标注
  17. 广州地铁线路查询程序C语言,广州地铁线路图
  18. 解决win11/win10无法安全拔出移动硬盘的问题 - U盘无法“安全删除并弹出媒体”
  19. SAP - 采购价格确定 ①
  20. 239页10万字“联、管、用”三位一体雪亮工程整体建设方案

热门文章

  1. curl php 模拟来源_PHP-Curl模拟HTTPS请求
  2. 前端接收pdf文件_雷达接收机的噪声系统及灵敏度
  3. electron 屏幕标注_屏幕 | screen (screen) – Electron 中文开发手册
  4. python数据科学指南是什么_《Python数据科学指南》——导读
  5. html如何显示带有记号的文本,如何使用Wicket设置HTML锚标记的显示文本?
  6. 究竟什么是Linux内核?我该如何高效学习?​
  7. 嵌入式牛人 | 这些单片机编程思想超硬核
  8. julia有 pytorch包吗_用 PyTorch 实现基于字符的循环神经网络 | Linux 中国
  9. 深井软岩巷道群支护技术与应用_引领支护创新,促进行业发展
  10. hash redis springboot_Redis常见的工作场景使用实战,Redisson分布式锁的实现