如何使用Android注解处理器

我们就可以结合今天的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。用来存放注解处理器。

创建一个继承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(可选)

注意两个端口号一致。然后选择添加的“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注解处理器的资料请关注其它相关文章!

以上是 如何使用Android注解处理器 的全部内容, 来源链接: utcz.com/p/243459.html

回到顶部