文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

怎么使用Android注解处理器

2023-06-14 12:45

关注

小编给大家分享一下怎么使用Android注解处理器,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

1.定义注解

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

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

public @interface LiveData {}

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

这里我的@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; // 用来输出日志、错误或警告信息@Overridepublic synchronized void init(ProcessingEnvironment processingEnv) {    super.init(processingEnv);    this.elementUtils = processingEnv.getElementUtils();    this.filer = processingEnv.getFiler();    this.messager = processingEnv.getMessager();}

下面就是重点process了,我们的注解作用范围是类和属性。所以我们需要将同一个类下的注解整理到一起。这里使用getElementsAnnotatedWith循环所有注解元素。

@Overridepublic 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);}

输出文件:

@Overridepublic 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(可选)

怎么使用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);  }}

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

以上是“怎么使用Android注解处理器”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网行业资讯频道!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯