前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Annotation Processor

Annotation Processor

作者头像
艳龙
发布2021-12-16 17:34:51
4360
发布2021-12-16 17:34:51
举报
文章被收录于专栏:yanlongli_艳龙yanlongli_艳龙

Annotation 和 Annotation Processor

要了解Annotation Processor,首先需要先了解什么是 AnnotationAnnotation : 是 Java 注解。 例如常见的 @Override @Nullable 等, 可以对类或者字段进行标记。 这些标记可以在反射时读取 或者 通过 Annotation Processor进行解析来自动生成一些对应的代码。

Annotation Processor: 注解处理器, 在代码编译前进行处理。 可以自动生成一些代码,来避免在编码时写一些重复代码, 例如findViewByid()

使用Annotation Processor的一些库: butterknife Dagger2 ...

这里通过一个学习的例子来了解Annotation Processor 的工作原理。

示例Demo

  1. Android Studio创建一个java library (lib_annotation), 用于自定义注解
  2. New -> Java Class -> 类型选择 Annotation

BindView.java

代码语言:javascript
复制
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface BindView {
    int value();
}



/*
*@Retention 是一个元注释,表明我们自定义注释的使用范围
*/
public enum RetentionPolicy {
    SOURCE,        //注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃
    CLASS,      //注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期, 
    RUNTIME;   //注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在, 运行时候可以配合反射一起使用

    private RetentionPolicy() {
    }
}


/*
*  @Target定义注解的作用目标
*/
public enum ElementType {
    TYPE,    //可以用在class上
    FIELD,   //字段
    METHOD,   // 方法  
    PARAMETER,  //方法参数
    CONSTRUCTOR, //构造函数
    LOCAL_VARIABLE,  //局部变量
    ANNOTATION_TYPE, //注解
    PACKAGE,   //包
    TYPE_PARAMETER,
    TYPE_USE;

    private ElementType() {
    }
}
  1. Android Studio再创建一个java library (lib_processor), 用于处理自定义注解

BindingProcessor.java

代码语言:javascript
复制
public class BindingProcessor extends AbstractProcessor {

    Filer filer;
    private Messager mMessager;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
        mMessager = processingEnv.getMessager();
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        mMessager.printMessage(Diagnostic.Kind.NOTE,"-------process start----");
        /*roundEnv.getRootElements()会返回工程中所有的Class
        在实际应用中需要对各个Class先做过滤以提高效率,避免对每个Class的内容都进行扫描*/
        for (Element element : roundEnvironment.getRootElements()) {
            // 获取包名
            String packageStr = element.getEnclosingElement().toString();
            // 获取类名称
            String classStr = element.getSimpleName().toString();
            mMessager.printMessage(Diagnostic.Kind.WARNING,"packageStr:" + packageStr + ", classStr:" + classStr);


            // 需要自动生成类的类名称
            ClassName className = ClassName.get(packageStr, classStr + "$Binding");
            // 构造函数的builder
            MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
                    .addModifiers(Modifier.PUBLIC)
                    .addParameter(ClassName.get(packageStr, classStr), "activity");

            boolean hasBinding = false;


            for (Element enclosedElement : element.getEnclosedElements()) {
                BindView bindView = enclosedElement.getAnnotation(BindView.class);
                if (bindView != null) {
                    hasBinding = true;
                    constructorBuilder.addStatement("activity.$N = activity.findViewById($L)",
                            enclosedElement.getSimpleName(), bindView.value());
                }
            }

            TypeSpec builtClass = TypeSpec.classBuilder(className)
                    .addModifiers(Modifier.PUBLIC)
                    .addMethod(constructorBuilder.build())
                    .build();

            if (hasBinding) {
                try {
                    JavaFile.builder(packageStr, builtClass)
                            .build().writeTo(filer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return false;
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new HashSet<>();
        types.add(BindView.class.getCanonicalName());
        return types;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.RELEASE_8;
    }
}
  1. lib_processor 模块中向系统注册Annotation Process。

resources -> META-INF -> services -> javax.annotation.processing.Processor

代码语言:javascript
复制
com.******.BindingProcessor   // 解析器的完整路径
  1. 自动生成的代码,可以通过反射进行调用。

BindViewUtils.java

代码语言:javascript
复制
  public static void bind(Activity activity) {
        try {
            // new MainActivityBinding(activity);
            Class bindingClass = Class.forName(activity.getClass().getCanonicalName() + "$Binding");
            Class activityClass = Class.forName(activity.getClass().getCanonicalName());
            Constructor constructor = bindingClass.getDeclaredConstructor(activityClass);
            constructor.newInstance(activity);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

可能遇到的问题

  • compileJava 编译出现警告
代码语言:javascript
复制
> Task :lib_process:compileJava
警告: [options] 未与 -source 1.7 一起设置引导类路径
1 个警告

问题原因: 本机的jdk环境 与 工程的配置环境不匹配

解决方案:

  1. 查看本机的java 版本
代码语言:javascript
复制
$ /usr/libexec/java_home -V
Matching Java Virtual Machines (1):
    1.8.0_191, x86_64:  "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home
  1. 修改工程配置中的Java 环境配置

build.gradle

代码语言:javascript
复制
sourceCompatibility = "8"
targetCompatibility = "8"
  • 注册processor 没有生效 向系统注册Annotation Process时,如果Processor 中process.java 未被执行。

resources -> META-INF -> services -> javax.annotation.processing.Processor

请检查文件路径是否正确, 本人就因为路径 META-INF 写成了 META_INF 导致注册失败。

END!

本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020/2/17 上,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Annotation 和 Annotation Processor
  • 示例Demo
  • 可能遇到的问题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com