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);
private int id;
private String name;
private boolean is;
构造代码与生成结果示例2:
MethodSpec.Builder buildMethodBuilder = MethodSpec.methodBuilder(“build”)
.addModifiers(Modifier.PUBLIC)
.returns(ClassName.get(“android.content”, “Intent”));
buildMethodBuilder.addParameter(ClassName.get(“android.content”, “Context”), “context”);
buildMethodBuilder.addStatement(String.format(“Intent intent = new Intent(context, %s.class)”, classHolder.getClassName()));
for (FieldHolder field : fields) {
buildMethodBuilder.addStatement(String.format(“intent.putExtra(”%s", %s)", field.getName(), field.getName()));
}
buildMethodBuilder.addCode(“if (!(context instanceof $T)) {\n”, ClassName.get(“android.app”, “Activity”));
buildMethodBuilder.addStatement(“intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)”);
buildMethodBuilder.addCode(“}\n”);
buildMethodBuilder.addStatement(“return intent”);
contentBuilder.addMethod(buildMethodBuilder.build());
public Intent build(Context context) {
Intent intent = new Intent(context, TestActivity.class);
intent.putExtra(“id”, id);
intent.putExtra(“name”, name);
intent.putExtra(“is”, is);
if (!(context instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
return intent;
}
构造代码与生成结果示例3:
String fieldTypeName = field.getType().toString();
3if (int.class.getName().equals(fieldTypeName)
|| Integer.class.getName().equals(fieldTypeName)) {
builder.addStatement(String.format(“target.%s = intent.getIntExtra(”%s", 0)", field.getName(), field.getName()));
} else if (String.class.getName().equals(fieldTypeName)) {
builder.addStatement(String.format(“target.%s = intent.getStringExtra(”%s")", field.getName(), field.getName()));
} else if (boolean.class.getName().equals(fieldTypeName)
|| Boolean.class.getName().equals(fieldTypeName)) {
builder.addStatement(String.format(“target.%s = intent.getBooleanExtra(”%s", false)", field.getName(), field.getName()));
}
public void bind(TestActivity target, Intent intent) {
target.id = intent.getIntExtra(“id”, 0);
target.name = intent.getStringExtra(“name”);
target.is = intent.getBooleanExtra(“is”, false);
}
7.将生成好的代码写入文件
JavaFile javaFile = JavaFile.builder(classHolder.getPackageName(), contentBuilder.build()).build();
try {
javaFile.writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
先自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以扫码领取!!!!
![](https://img-blog.csdnimg.cn/img_convert/649657b397ee2c5d155da02741c46ffd.jpeg)
学习分享,共勉
Android高级架构师进阶之路
题外话,我在阿里工作多年,深知技术改革和创新的方向,Android开发以其美观、快速、高效、开放等优势迅速俘获人心,但很多Android兴趣爱好者所需的进阶学习资料确实不太系统,完整。今天我把我搜集和整理的这份学习资料分享给有需要的人
- Android进阶知识体系学习脑图
- Android进阶高级工程师学习全套手册
- 对标Android阿里P7,年薪50w+学习视频
- 大厂内部Android高频面试题,以及面试经历
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可免费领取!
roid进阶知识体系学习脑图**
[外链图片转存中…(img-j4STUwcX-1711290760308)]
- Android进阶高级工程师学习全套手册
[外链图片转存中…(img-n9uADtsB-1711290760308)]
- 对标Android阿里P7,年薪50w+学习视频
[外链图片转存中…(img-hxMx5bsZ-1711290760309)]
- 大厂内部Android高频面试题,以及面试经历
[外链图片转存中…(img-PVQDpp1q-1711290760309)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可免费领取!