如何使用Android注解处理器

作者:唯鹿 时间:2023-08-16 16:49:05 

我们就可以结合今天的Annotation Processing Tool(APT)来自定义注解处理器。

注解处理器简单解释就是收集我们标记的注解,处理注解上提供的信息。

本篇用我之前写的Saber举例说明。

1.定义注解

推荐New -> Module -> Java Library,新建一个Java Library Module,命名为xx-annotation。用来单独存放注解。

既然是注解处理器,那么首先需要有注解。自定义一个注解使用@interface关键字。


public @interface LiveData {
}

然后我们需要用到注解的注解,也就是元注解来控制注解的行为。这里我简单介绍一些元注解。

  • Retention 表示注解的保留范围。值用RetentionPolicy枚举类型表示,分为CLASSRUNTIMESOURCE

  • 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。用来存放注解处理器。

如何使用Android注解处理器

创建一个继承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的类型。

ElementTypeElementKindElement
TYPECLASSTypeElement
FIELDFIELDVariableElement
METHODMETHODExecutableElement

下面是封装的简易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(可选)

如何使用Android注解处理器

注意两个端口号一致。然后选择添加的“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);
 }
}

至此,我们就完成了一个简单的自定义处理器。

来源:https://blog.csdn.net/qq_17766199/article/details/113816175

标签:Android,注解处理器,注解
0
投稿

猜你喜欢

  • springboot+mybatis+redis 二级缓存问题实例详解

    2022-08-09 09:06:36
  • spring中bean id相同引发故障的分析与解决

    2023-08-05 11:30:41
  • Jersey Restful接口如何获取参数的问题

    2023-10-29 14:44:16
  • Android拍照上传功能示例代码

    2022-08-22 07:51:39
  • C#获取存储过程返回值和输出参数值的方法

    2021-10-19 14:45:22
  • Java接口RandomAccess全面了解

    2023-09-11 19:05:29
  • C# .NET 中的缓存实现详情

    2023-05-30 18:35:12
  • Android编程自定义线程池与用法示例

    2021-11-27 14:46:53
  • 详解利用SpringCloud搭建一个最简单的微服务框架

    2023-08-21 04:24:32
  • Android Retrofit文件下载进度显示问题的解决方法

    2021-09-23 20:24:10
  • java中@SuppressWarnings注解用法详解

    2023-09-20 23:11:40
  • 手把手教你SpringBoot轻松整合Minio

    2022-07-24 14:00:37
  • 完全解析Android多线程中线程池ThreadPool的原理和使用

    2022-04-07 20:19:40
  • JFreeChart插件实现的折线图效果实例

    2023-09-21 02:20:03
  • Android Studio 生成自定义jar包的步骤详解

    2023-01-31 05:02:41
  • Java网络编程TCP实现聊天功能

    2023-12-01 17:05:35
  • Android编程中Tween动画和Frame动画实例分析

    2023-12-03 06:31:56
  • Java经典排序算法之插入排序

    2022-08-29 11:08:39
  • Java动态代理模式的深入揭秘

    2023-07-27 23:41:25
  • Java实现的微信图片处理工具类【裁剪,合并,等比例缩放等】

    2022-03-26 11:32:59
  • asp之家 软件编程 m.aspxhome.com