Android注解研习录

概要

在Java和Android的开发中,自从注解问世,越来越受开发者们所青睐,可以说,注解大大简化了开发者的开发作业。无论是运行时注解,还是编译时注解,都被广泛的使用着。而且,市面上产生了好多依赖注解而崛起的开源库,Google官方更是为了Android而推出了Support Annotation。

如何定义注解

注解是Java引入的特性,通过在Java源代码中引入注解标签,可以在编译或者运行期间做一些额外的事情。每个注解都有严格的格式来定义:@interface用来声明注解,接口的方法对应于注解的元素,下面是一个完整的注解定义:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Bind {
    @IdRes int[] value();
}

@interface声明会创建一个真实的java接口,与其它接口一样,会被编译成.class文件;注解接口中的元素实际上是方法声明(如上面的value()方法),这个方法声明没有参数,没有throws语句,也不能使用泛型。

标准注解

Java中默认定义的注解称之为标准注解,他们在java.lang、java.lang.annotation和javax.annotation包中,根据应用的场景,可以分为三类:

1、与编译相关的注解:给编译器使用

  • @Override:编译器会检查被注解的方法是否重载了一个父类的方法,如果没有,编译器会给出错误提示;
  • @Deprecated:用来修饰任何不被鼓励使用或者放弃使用的方法或属性;
  • @FunctionalInterface:用来修饰接口,表示对应的接口是带单个方法的函数式接口;
  • @SafeVarargs:用于方法或构造函数,用来断言不定长参数可以安全使用;
  • @SuppressWarnings:用来抑制某种类型的警告;
  • @Generated:一般用来给代码生成工具使用,表示这段代码是自动生成的,而不是开发者自己编写的,通常这段代码不建议修改;

2、资源相关的注解:一共有4个,一般用于JavaEE,Android领域不会用到,具体如下:

  • @PostConstruct:用在控制对象生命周期的环境中,表示在构造函数之后应立即调用被修饰的方法;
  • @PreDestroy:表示在删除一个被注入的对象前应立即调用被该注解修饰的方法;
  • Resource:用于Web容器的资源注入,表示单个资源;
  • Resources:用于Web容器的资源注入,表示一个资源;

3、元注解:用于定义和实现注解的注解,一个有5种:

  • @Target:这个注解的元素是一个ElementType类型的数组,表示这个注解所适用的对象范围,总共有10种类型,分别如下:
public enum ElementType {
    TYPE,              //类或接口(包含eumn和注解类型的接口)
    FIELD,             //实例变量
    METHOD,            //方法
    PARAMETER,         //参数
    CONSTRUCTOR,       //构造函数
    LOCAL_VARIABLE,    //局部变量
    ANNOTATION_TYPE,   //注解类型声明
    PACKAGE,           //包
    TYPE_PARAMETER,    //类型参数
    TYPE_USE;          //类型的用途
    private ElementType() {
    }
}
  • @Retention:用于指定注解的访问范围,即在什么级别保留注解,有三种类型,分别为:
public enum RetentionPolicy {
    SOURCE,   //源码级注解,只会保留在.java文件
    CLASS,    //编译级注解,编译后会保留在.java和.class文件,运行时被虚拟机丢弃,默认选项
    RUNTIME;  //运行时注解,在运行期间也会保留注解,可以通过反射机制读取注解信息
    private RetentionPolicy() {
    }
}
  • @Documented:表示被修饰的注解应该被包含在被注解项的文档中;
  • @Inherited:表示该注解可以被之类继承;
  • @Repeatable:表示这个注解可以被应用多次;

编译时注解

编译时注解能够让编译器自动处理Java源文件并生成更多的代码、配置文件、脚本或其它可能想要生成的东西。这些操作通过注解处理器完成。Java编译器集成了注解处理器,通过在编译期间调用javac -processor命令可以调用注解处理器,它能够允许我们实现编译时注解的功能,从而提高函数库的性能。

1、定义注解处理器
编译期间,编译器会定位到java源文件中的注解,然后由注解处理器进行处理,需要注意的是,注解处理器只会生成新的源文件,而不会去修改一个已经存在的源文件。注解处理器通常继承AbstractProcessor类并实现process()方法,同时需要指定这个处理器能够处理的注解类型以及支持的Java版本。如下:

package com.example;

public class MyProcessor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment env){ }

    @Override
    public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }

    @Override
    public Set<String> getSupportedAnnotationTypes() { }

    @Override
    public SourceVersion getSupportedSourceVersion() { }

}
  • init(ProcessingEnvironment env): 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。ProcessingEnviroment提供很多有用的工具类Elements,Types和Filer。
  • getSupportedAnnotationTypes():指定这个注解处理器能够处理的注解类型,返回一个可以支持的注解类型的字符串集合;从java7开始,可以使用@SupportedAnnotationTypes({})注解来代替该方法;
  • getSupportedSourceVersion():指定注解处理器适用的Java版本,通常返回SourceVersion.latestSupported()即可,当然也可以返回指定版本的Java,如SourceVersion.RELEASE_6;从java7开始,可以使用@SupportedSourceVersion(Java_Version)注解来代替该方法;
  • process(Set<? extends TypeElement> annoations, RoundEnvironment env):在该方法中实现注解处理器的具体业务逻辑,根据env可以得到具体的被注解元素,然后可以编写处理注解的代码最终生成所需的Java源文件;

2、注册注解处理器

注解处理器定义好之后,为了让javac -processor能够对其进行处理,我们需要将注解处理器打包成一个.jar文件,同时,需要在jar文件中增加一个名为javax.annotation.processing.Processor的文件来指明jar文件中都包含哪些注解处理器,这个文件的最终路径位于jar文件根目录的META-INF/services目录中;javax.annotation.processing.Processor文件的内容是注解处理器的全路径名,如果存在多个,以换行进行隔离,如下:

com.heyha.processor.JsonAnnotationProcessor

现在,我们知道了该文件的最终路径,那么它的源文件的路径是怎样的呢?只需要在src/main/java同级目录中新建一个名为resources的目录,将javax.annotation.processing.Processor文件放进去即可。
此外,注解处理器所在的工程必须是Java Library类型,而不能是Android Library类型,因为在编译时注解可能会用到javax包里面的内容。手动执行这个注册过程是很繁琐的,为了简化该操作,google官方提供了AutoService函数库,使用方式是引入该函数库后,在定义Processor时添加AutoService注解标记即可。如下:

@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({
        //注解处理器支持的所有注解全名字符串类型的集合
})
public class MyProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        return false;
    }
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
    }
}

还有一点需要注意,注解处理器所在的jar包只在编译期间起作用,运行时不起作用,所以在使用该jar包时需要在build.gradle中使用provided方式引入,而不是compile方式。

注:本篇文章是为了记录学习Android注解的使用,在此感谢顾老师的《Android高级进阶》。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值