我们就可以结合今天的Annotation Processing Tool
(APT)来自定义注解处理器。
注解处理器简单解释就是收集我们标记的注解,处理注解上提供的信息。
本篇用我之前写的Saber举例说明。
1.定义注解
推荐New -> Module -> Java Library,新建一个Java Library Module,命名为xx-annotation。用来单独存放注解。
既然是注解处理器,那么首先需要有注解。自定义一个注解使用@interface
关键字。
- public @interface LiveData {
- }
然后我们需要用到注解的注解,也就是元注解来控制注解的行为。这里我简单介绍一些元注解。
- Retention 表示注解的保留范围。值用
RetentionPolicy
枚举类型表示,分为CLASS
、RUNTIME
、 SOURCE
。
Target
表示注解的使用范围。值用ElementType
枚举类型表示,有TYPE
(作用于类)、FIELD
(作用于属性)、METHOD
(作用于方法)等。
这里我的@LiveData
注解作用是为了便于创建LiveData
,而创建时需要知道数据类型。所以这个注解的使用范围就是类和属性。
其次这个注解处理生成模板代码后,我们不需要保留在编译后的.class文件中。所以可以使用SOURCE
。
- @Retention(RetentionPolicy.SOURCE)
- @Target({ElementType.FIELD, ElementType.TYPE})
- public @interface LiveData {
- }
-
2.实现处理器
首先New -> Module -> Java Library,新建一个Java Library Module,命名为xx-complier。用来存放注解处理器。

创建一个继承AbstractProcessor
的类LiveDataProcessor
。
- public class LiveDataProcessor extends AbstractProcessor {
-
- @Override
- public SourceVersion getSupportedSourceVersion() {
- return SourceVersion.latestSupported();
- }
-
- @Override
- public Set<String> getSupportedAnnotationTypes() {
- return Collections.singleton(LiveData.class.getCanonicalName());
- }
-
- @Override
- public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- return false;
- }
- }
需要实现三个方法,getSupportedSourceVersion
指定支持的Java版本,getSupportedAnnotationTypes
指定处理的注解。process
是处理注解的地方。
不过这里还需要初始化一些工具,可以重写init
来实现。
- private Elements elementUtils; // 操作元素的工具类
- private Filer filer; // 用来创建文件
- private Messager messager; // 用来输出日志、错误或警告信息
-
- @Override
- public synchronized void init(ProcessingEnvironment processingEnv) {
- super.init(processingEnv);
- this.elementUtils = processingEnv.getElementUtils();
- this.filer = processingEnv.getFiler();
- this.messager = processingEnv.getMessager();
- }
下面就是重点process
了,我们的注解作用范围是类和属性。所以我们需要将同一个类下的注解整理到一起。这里使用getElementsAnnotatedWith
循环所有注解元素。
- @Override
- public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
-
- for (Element element : roundEnv.getElementsAnnotatedWith(LiveData.class)) {
- if (element.getKind() == ElementKind.FIELD) {
- handlerField((VariableElement) element); // 表示一个字段
- }
- if (element.getKind() == ElementKind.CLASS) {
- handlerClass((TypeElement) element); // 表示一个类或接口
- }
- // ExecutableElement表示某个类或接口的方法
- }
- return true;
- }
-
- private void handlerClass(TypeElement element) {
- ClassEntity classEntity = new ClassEntity(element);
- String className = element.getSimpleName().toString();
-
- if (classEntityMap.get(className) == null) {
- classEntityMap.put(className, classEntity);
- }
- }
-
- private void handlerField(VariableElement element) {
- FieldEntity fieldEntity = new FieldEntity(element);
- String className = fieldEntity.getClassSimpleName();
- if (classEntityMap.get(className) == null) {
- classEntityMap.put(className,
- new ClassEntity((TypeElement) element.getEnclosingElement()));
- }
- ClassEntity classEntity = classEntityMap.get(className);
- classEntity.addFieldEntity(fieldEntity);
- }
上面代码中的element.getKind()
获取的是元素种类,对应的基本是上面元注解ElementType
的类型。
ElementType |
ElementKind |
Element |
TYPE |
CLASS |
TypeElement |
FIELD |
FIELD |
VariableElement |
METHOD |
METHOD |
ExecutableElement |
下面是封装的简易element,便于实际的使用。
- class ClassEntity {
- private final TypeElement element;
- private final Name classSimpleName;
- private final Map<String, FieldEntity> fields = new HashMap<>();
-
- public ClassEntity(TypeElement element) {
- this.element = element;
- this.classSimpleName = element.getSimpleName();
- }
-
- public String getClassSimpleName() {
- return classSimpleName.toString();
- }
-
- public void addFieldEntity(FieldEntity fieldEntity) {
- String fieldName = fieldEntity.getElement().toString();
- if (fields.get(fieldName) == null) {
- fields.put(fieldName, fieldEntity);
- }
- }
-
- public TypeElement getElement() {
- return element;
- }
-
- public Map<String, FieldEntity> getFields() {
- return fields;
- }
- }
-
- class FieldEntity {
- private VariableElement element;
- private String classSimpleName;
-
- public FieldEntity(VariableElement element) {
- this.element = element;
- this.classSimpleName = element.getEnclosingElement().getSimpleName().toString();
- }
- public VariableElement getElement() {
- return element;
- }
-
- public String getClassSimpleName() {
- return classSimpleName;
- }
- }
下面就是使用JavaPoet来生成代码,具体使用见JavaPoet使用攻略。这部分直接上代码:
- private JavaFile brewViewModel(Map.Entry<String, ClassEntity> item) {
- ClassEntity classEntity = item.getValue();
- LiveData liveData = classEntity.getElement().getAnnotation(LiveData.class);
- /*类名*/
- String className = classEntity.getElement().getSimpleName().toString() + "ViewModel";
-
- ClassName viewModelClazz = ClassName.get("androidx.lifecycle", "ViewModel");
-
-
- TypeSpec.Builder builder = TypeSpec
- .classBuilder(className)
- .addModifiers(Modifier.PUBLIC)
- .superclass(viewModelClazz);
-
- // 优先执行类LiveData注解
- if (liveData != null){
- TypeName valueTypeName = ClassName.get(classEntity.getElement());
- brewLiveData(classEntity.getClassSimpleName(), valueTypeName, builder);
- }else {
- Map<String, FieldEntity> fields = classEntity.getFields();
-
- for (FieldEntity fieldEntity : fields.values()){
- String fieldName = StringUtils.upperCase(fieldEntity.getElement().getSimpleName().toString());
- TypeName valueTypeName = ClassName.get(fieldEntity.getElement().asType());
- brewLiveData(fieldName, valueTypeName, builder);
- }
- }
-
- TypeSpec typeSpec = builder.build();
- // 指定包名
- return JavaFile.builder("com.zl.weilu.saber.viewmodel", typeSpec).build();
- }
-
- private void brewLiveData(String fieldName, TypeName valueTypeName, TypeSpec.Builder builder){
-
- String liveDataType;
- ClassName liveDataTypeClassName;
-
- liveDataType = "m$L = new MutableLiveData<>()";
- liveDataTypeClassName = ClassName.get("androidx.lifecycle", "MutableLiveData");
-
- ParameterizedTypeName typeName = ParameterizedTypeName.get(liveDataTypeClassName, valueTypeName);
-
- FieldSpec field = FieldSpec.builder(typeName, "m" + fieldName, Modifier.PRIVATE)
- .build();
-
- MethodSpec getMethod = MethodSpec
- .methodBuilder("get" + fieldName)
- .addModifiers(Modifier.PUBLIC)
- .returns(field.type)
- .beginControlFlow("if (m$L == null)", fieldName)
- .addStatement(liveDataType, fieldName)
- .endControlFlow()
- .addStatement("return m$L", fieldName)
- .build();
-
- MethodSpec getValue = MethodSpec
- .methodBuilder("get" + fieldName + "Value")
- .addModifiers(Modifier.PUBLIC)
- .returns(valueTypeName)
- .addStatement("return this.$N().getValue()", getMethod)
- .build();
-
- MethodSpec setMethod = MethodSpec
- .methodBuilder("set" + fieldName)
- .addModifiers(Modifier.PUBLIC)
- .returns(void.class)
- .addParameter(valueTypeName, "mValue")
- .beginControlFlow("if (this.m$L == null)", fieldName)
- .addStatement("return")
- .endControlFlow()
- .addStatement("this.m$L.setValue(mValue)", fieldName)
- .build();
-
- MethodSpec postMethod = MethodSpec
- .methodBuilder("post" + fieldName)
- .addModifiers(Modifier.PUBLIC)
- .returns(void.class)
- .addParameter(valueTypeName, "mValue")
- .beginControlFlow("if (this.m$L == null)", fieldName)
- .addStatement("return")
- .endControlFlow()
- .addStatement("this.m$L.postValue(mValue)", fieldName)
- .build();
-
- builder.addField(field)
- .addMethod(getMethod)
- .addMethod(getValue)
- .addMethod(setMethod)
- .addMethod(postMethod);
-
- }
输出文件:
- @Override
- public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- ...
- for (Map.Entry<String, ClassEntity> item : classEntityMap.entrySet()) {
- try {
- brewViewModel(item).writeTo(filer);
- } catch (Exception e) {
- messager.printMessage(Diagnostic.Kind.ERROR, e.getMessage(), item.getValue().getElement());
- }
- }
- return true;
- }
3.注册处理器
注册处理器才可以使处理器生效,使用Google开源的AutoService的库。
- dependencies {
- implementation 'com.squareup:javapoet:1.13.0'
- implementation 'com.google.auto.service:auto-service:1.0-rc7'
- annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'
- }
然后添加@AutoService
注解即可。
- @AutoService(Processor.class)
- public class LiveDataProcessor extends AbstractProcessor {
-
- }
4.调试注解处理器
项目的gradle.properties
中配置:
- org.gradle.daemon=true
- org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
接着Run -> Edit Configurations -> 点击左上角加号 -> 选择 Remote -> 指定module(可选)

注意两个端口号一致。然后选择添加的“APT”,点击debug按钮启动。
后面就是打断点,编译项目即可。
5.支持增量编译
Gradle 在 5.0 增加了对 Java 增量编译的支持,通过增量编译,我们能够获得一些优点:
如果注解处理器没有支持增量编译,那么编译时,会输出以下日志:
- w: [kapt] Incremental annotation processing requested, but support is disabled because the following processors are not incremental: com.x.XXProcessor (NON_INCREMENTAL).
Gradle 支持两种注解处理器的增量编译:isolating 和 aggregating。
支持方法是在 META-INF/gradle/incremental.annotation.processors
文件中声明支持增量编译的注解处理器。
- xx-complier/src/main/
- ├── java
- │ ...
- │ └── LiveDataProcessor
- └── resources
- └── META-INF
- ├── gradle
- │ └── incremental.annotation.processors
- └── services
- └── javax.annotation.processing.Processor
incremental.annotation.processors
内容如下:
- com.zl.weilu.saber.compiler.LiveDataProcessor,aggregating
这部分详细内容见 让 Annotation Processor 支持增量编译。
6.使用处理器
添加依赖:
- dependencies {
- implementation project(':saber-annotation')
- annotationProcessor project(':saber-compiler')
- }
首先创建一个类,使用@LiveData注解标记你要保存的数据。
- public class SeekBar {
-
- @LiveData
- Integer value;
- }
Build – > Make Project 生成代码如下:
- public class SeekBarViewModel extends ViewModel {
- private MutableLiveData<Integer> mValue;
-
- public MutableLiveData<Integer> getValue() {
- if (mValue == null) {
- mValue = new MutableLiveData<>();
- }
- return mValue;
- }
-
- public Integer getValueValue() {
- return getValue().getValue();
- }
-
- public void setValue(Integer mValue) {
- if (this.mValue == null) {
- return;
- }
- this.mValue.setValue(mValue);
- }
-
- public void postValue(Integer mValue) {
- if (this.mValue == null) {
- return;
- }
- this.mValue.postValue(mValue);
- }
- }
至此,我们就完成了一个简单的自定义处理器。
以上就是如何使用Android注解处理器的详细内容,更多关于Android注解处理器的资料请关注w3xue其它相关文章!