上一篇写了JavaPoet使用攻略,了解了JavaPoet用法。那么我们就可以结合今天的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=trueorg.gradle.jvmargs=-agentlib:jDWp=transport=dt_socket,server=y,suspend=n,address=5005
接着Run -> Edit Configurations -> 点击左上角加号 -> 选择 Remote -> 指定module(可选)
后面就是打断点,编译项目即可。
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); }}
至此,我们就完成了一个简单的自定义处理器。
7.参考注解处理器(APT)了解一下
详尽的Android编译时注解处理器教程
让 Annotation Processor 支持增量编译
总结以上是内存溢出为你收集整理的Android 注解处理器使用攻略全部内容,希望文章能够帮你解决Android 注解处理器使用攻略所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)