如何在-Android-中完成一个-APT-项目的开发?

在TestActivity中,我们期望调用:

new TestActivityFastBundle().bind(this, getIntent());

实现自动将Intent中的变量赋值给当前类中的变量。

###搭建APT项目

1.创建一个Android Library,并创建自己需要的注解类。

举例:

@Retention(CLASS)
@Target(FIELD)
public @interface AutoBundle {
boolean require() default false;
}

2.创建一个Java Library,引用步骤1中所创建的Android Library,并为这个Java Library添加依赖。

implementation ‘com.google.auto.service:auto-service:1.0-rc2’

介绍一下这个库是做什么用的:

因为注解处理器是在编译期间进行工作,需要向编译器进行“注册”,让编译器知道需要使用哪个注解器处理数据。

如果不使用auto-service库,那么手动注册的方法如下:

1.在Library中创建resources文件夹;
2.在resources中创建META-INF和services两个文件夹;
3.在services中创建一个文件,命名为javax.annotation.processing.Processor;
4.在javax.annotation.processing.Processor文件中输入自己所创建的注解处理器类名(完整的,包括包名)。

3.创建自己的处理类,继承AbstractProcessor,并使用auto-service注册。

举例:

@AutoService(Processor.class)
public class AutoBundleProcessor extends AbstractProcessor

在创建AbstractProcessor子类后,我们需要重写其中的几个方法,来实现自己的处理逻辑:

@Override
public synchronized void init(ProcessingEnvironment processingEnvironment)

Processor的初始化方法,在编译阶段会首先回调此方法,ProcessingEnvironment类包含了解析需要的数据对象,我们可以通过它获取到一系列我们需要的其他对象,进而获取到需要的数据。

@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment)

process方法在编译过程中回调,在此我们可以获取到我们需要的类、对象及其对应的注解,在此可以分析并处理数据,最终生成我们需要的代码。

@Override
public Set getSupportedAnnotationTypes()

getSupportedAnnotationTypes方法帮助我们获得所需要的注解类。我们将自己需要的类名放入Set中并返回给注解处理器,换句话说,在这里为注解处理器指定需要处理哪些注解。

4.在项目中引用

在主项目的gradle中引用包含注解的Android Library引用注解器所在的Java Library。由于kotlin的引入,建议使用kapt而非annotationProcessor。

举例:

kapt project(‘:libProce’)

至此,工程整体结构已经搭建完成。

后续将介绍APT中各种类和对象的作用,以及如何实现我们需要的功能。

###APT中的数据类型与概念

####1.ProcessingEnvironment

当我们在子类中复写了AbstractProcessor的init方法时,其参数就是一个ProcessingEnvironment对象。它内部提供了实用的对象,如Elements、Types、Filer,在APT过程中都具有重要作用。我们可以获取到这些对象,来实现我们需要的功能。

####2.Element

在APT阶段,任何事物都被称为元素。比如一个对象、一个类、一个方法、一个参数。在APT中,它们都被统一称为元素。Element本身是一个接口,也有多个子类,比如TypeElement、VariableElement,子类在其基础上增加了额外的接口方法来描述具体事物的特殊属性。

####3.ElementKind

由于在APT中,任何事物都被称为元素,所以我们需要知道某个元素究竟是什么,这时候可以通过ElementKind判断。

ElementKind是一个枚举类。其中包括但不限于PACKAGE(包)、CLASS(类)、INTERFACE(接口)、FIELD(变量)、PARAMETER(参数)、METHOD(方法)等。这些都是我们开发中的基本概念。

####4.Elements

Elements可以理解为一个工具类,它的功能就是操作Element对象,对Element对象进行一些处理或取值。

####5.TypeElement

TypeElement是Element子类,它表示这个元素是一个类或者接口。当Element满足条件时候,可以强转为一个TypeElement对象。

####6.VariableElement

VariableElement是Element子类,它表示这个元素是一个变量、常量、方法、构造器、参数等。当Element满足条件时候,可以强转为一个VariableElement对象。

####7.Filer

Filer是一个文件操作的接口,它可以创建或写入一个Java文件。主要针对的是Java文件对象,和一般文件的区别在于这是专门处理Java类文件的,以.java或.class为后缀的文件。在APT过程中,如果我们自动化代码生成完毕,需要生成一个.java或.class文件的时候,就需要用到Filer。

####8.Name

Name类是CharSequence的子类,主要表示类名、方法名。大部分情况下可以认为它和String等价。

####9.Types

Types可以理解为一个工具类,是类型操作工具,在APT阶段,我们需要知道一个变量是int还是boolean,那将需要通过Types相关类处理。它可以操作TypeMirror对象。

####10.TypeMirror

TypeMirror表示数据类型。比如基本类型int、boolean,也可以表示复杂数据类型,比如自定义类、数组、Parcelable等。

####11.Modifier

即修饰词。比如声明一个变量时候,private static final这些均为修饰词。大部分被Android Studio标示为蓝色的都是修饰词(除了class int interface这些)。

注:如果一个类中的变量缺省作用范围,那么修饰词为default。

####12.RoundEnvironment

当我们在子类中复写了AbstractProcessor的process方法时,其参数就是一个RoundEnvironment对象。可以通过RoundEnvironment对象获取到我们在代码中设置了相关注解的Element。

###APT处理过程拆解

下面将以上文中所举出的场景,逐步对APT处理过程进行拆解,最终获取到我们需要的属性,为生成自动化代码做准备。

在TestActivity中的变量上设置注解:

@AutoBundle
public int id;
@AutoBundle
public String name;
@AutoBundle
public boolean is;

其中AutoBundle注解是我们自己定义的注解类。

初步设计好后,我们需要在process方法中重写我们的逻辑:

@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment)

第一步:

获取所有被AutoBundle注解所声明的元素。这里我们知道,其实只有三个变量;

for (Element element : roundEnvironment.getElementsAnnotatedWith(AutoBundle.class)) {

}

第二步:

对每个循环中的Element对象,获取其数据信息;

if (element.getKind() == ElementKind.FIELD) {
// 可以安全地进行强转,将Element对象转换为一个VariableElement对象
VariableElement variableElement = (VariableElement) element;
// 获取变量所在类的信息TypeElement对象
TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();

variableElement中包含的数据包括修饰词、类型、变量名等;

typeElement中包含的数据包括类名、包名等。

// 获取类名
String className = typeElement.getSimpleName().toString();

// 获取包名
String packageName = elements.getPackageOf(typeElement).getQualifiedName().toString();

// 获取变量上的注解信息
AutoBundle autoBundle = variableElement.getAnnotation(AutoBundle.class);
boolean require = autoBundle.require();

// 获取变量名
Name name = variableElement.getSimpleName();

// 获取变量类型
TypeMirror type = variableElement.asType();

对于我们上文定义的某个变量,比如:

@AutoBundle(require = true)
public int id;

那么获取到数据后:

require = true
name = “id”
type = int.class

其他两个变量同理。

三次循环将获取到我们需要的所有信息。

包括三个变量的注解值、变量名、类型。同时我们也获取到了TestActivity的类名和包名。可以对这些数据进行一些封装和缓存。接下来就可以自动化生成代码了。

我将上述变量值封装为ClassHoder与FieldHolder类中,ClassHolder保存了类名、包名等信息,FieldHolder保存了每个变量类型、变量名、注解等信息。下面将用这些保存好的数据,通过JavaPoet生成代码。

###JavaPoet代码自动化生成

JavaPoet是Java代码自动生成框架,是一个github上的开源项目,地址:https://github.com/square/javapoet 。

JavaPoet简化了Java代码生成的开发难度,通过建造者模式,使调用更加人性化,可读性提升。具有自动import的功能,不需要再手动指定。

JavaPoet中,大部分数据类型使用了APT中通用的类型,结合APT自动化产生代码非常方便快速。

1.TypeSpec.Builder

TypeSpec.Builder是类的构建类,这里的类是广义上的,可以是一个class、interface、annotation等。

示例代码:

TypeSpec.Builder contentBuilder = TypeSpec.classBuilder(“yourClassName”)

2.MethodSpec.Builder

MethodSpec.Builder是方法的构建类。

示例代码:

MethodSpec.Builder bindMethodBuilder = MethodSpec.methodBuilder(“yourMethodName”)

3.FieldSpec.Builder

FieldSpec.Builder是变量的构建类。

示例代码:

FieldSpec.Builder fieldBuilder = FieldSpec.builder(ClassName.get(field.getType()), “yourFieldName”, Modifier.PRIVATE)

4.JavaFile.Builder

示例代码:

JavaFile javaFile = JavaFile.builder(classHolder.getPackageName(), contentBuilder.build())
javaFile.writeTo(mFiler);

5.各类Builder的方法

6.代码生成示例

构造代码与生成结果示例1:

for (FieldHolder field : fields) {
FieldSpec f = FieldSpec.builder(ClassName.get(field.getType()), field.getName(), Modifier.PRIVATE)
.build();
contentBuilder.addField(f);

最后我想说

为什么很多程序员做不了架构师?
1、良好健康的职业规划很重要,但大多数人都忽略了
2、学习的习惯很重要,持之以恒才是正解。
3、编程思维没能提升一个台阶,局限在了编码,业务,没考虑过选型、扩展
4、身边没有好的架构师引导、培养。所处的圈子对程序员的成长影响巨大。

金九银十面试季,跳槽季,整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

…(img-KgGFqMNc-1714598259886)]

里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值