注解现在广泛的应用于android的各个开源框架中,不理解注解,我们就无法更好的提升我们的架构能力。那么什么是注解呢?注解(Annotation),是JDK5.0 引入的一种注释机制。 注解是元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。
在java中,所有的注解都默认实现Annotation接口,接口如下:
public interface Annotation {
boolean equals(Object var1);
int hashCode();
String toString();
Class extends Annotation> annotationType();
}
要定义一个注解,和定义一个接口差不多,只不过在interface前面多了一个@
public @interface YuanZhen{
}
在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为 meta-annotation(元注解)
元注解有四种类型:
@Target | 指定注解的作用范围 |
@Retention | 指定注解的作用时机 |
@Inherited | 被该注解修饰的注解,作用在某个类上,该类的此注解可以被子类继承 |
@Documented | 给Javadoc配置的 |
@IntDef | android新引入的,int类型,代替枚举的使用 |
@StringDef | android新引入的,String类型,代替枚举的使用 |
最常用的是@Target 和@Retention。@Inherited和@Documented 一般很少用到,@IntDef和@StringDef多用来代替枚举,因为枚举的使用会增加内存的消耗。
@Target用来指定注解的作用范围,参数如下:
ElementType.ANNOTATION_TYPE
|
可以应用于注解类型
|
ElementType.CONSTRUCTOR
|
可以应用于构造函数
|
ElementType.FIELD
|
可以应用于字段或属性
|
ElementType.LOCAL_VARIABLE
|
可以应用于局部变量
|
ElementType.METHOD
|
可以应用于方法级注解
|
ElementType.PACKAGE
|
可以应用于包声明
|
ElementType.PARAMETER
|
可以应用于方法的参数
|
ElementType.TYPE
|
可以应用于类的任何元素
|
可以看一下下面的例子:
定义一个作用于方法参数的注解
@Target(ElementType.PARAMETER)//作用于方法的参数
public @interface YuanZhen {
}
当作用于类上时,就会报错,而作用于方法参数上,就可以正常运行
此元注解用来指定注解的作用时机,也就是说注解是在什么阶段有效,参数如下:
RetentionPolicy.SOURCE
|
标记的注解仅保留在源级别中,并被编译器忽略
|
RetentionPolicy.CLASS
|
标记的注解在编译时由编译器保留,但 Java 虚拟机 (JVM) 会忽略
|
RetentionPolicy.RUNTIME
|
标记的注解由 JVM 保留,因此运行时环境可以使用它
|
源码级别,在编译期能够获取注解与注解声明的类包括类中所有成员信息,一般用于生成额外的辅助类。
这个最常用的技术就是APT技术,好多开源框架都是使用的这种技术,例如:ButterKnife,EventBus,ARout,IOC,ROOM等等,应用非常广泛,APT技术一般是结合JavaPoet使用,具体怎么使用,请参照android注解之APT和javapoet_袁震的博客-CSDN博客
字节码级别,在编译出Class后,通过修改Class数据以实现修改代码逻辑目的。对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解。
这个最常用的技术就是字节码插桩,字节码插桩框架主要有三种,ASM,Javassist,AspectJ。
性能方面ASM最优。稳定性和简单性方面AspectJ最优。利用AOP思想,我们可以无侵入的实现埋点,监控方法耗时,UI卡顿监控,自动添加日志,代码隔离等等功能。以后有时间会讲解各个功能的实现方案。
上面两个都属于编译阶段,RetentionPolicy.RUNTIME属于运行时期,它是在程序运行期间,通过反射技术动态获取注解与其元素,从而完成不同的逻辑判定。
这个最典型的使用案例就是反射,关于反射相信大家都不陌生了。很多老的框架都是使用的反射,比如XUtils等,比较影响性能,所以逐步被APT和JavaPoet技术替代。
该注解的作用是子类可以继承父类的注解,意思就是,如果一个注解添加了@Inherited,例如:
@Inherited
@Target(ElementType.TYPE)
public @interface YuanZhen {
}
那么用该注解修饰的类,其子类可以获取到该注解。
@IntDef 和@StringDef是安卓引入的新的元注解,这两个注解的主要作用是用来替代枚举的,因为枚举对内存的消耗比较大,下面来看怎么使用:
自定义一个注解:
@Inherited
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.SOURCE)
@IntDef({DataBean.DOCTOR, DataBean.TEACHER})
public @interface YuanZhen {
}
数据类:
public class DataBean {
public static final int DOCTOR = 0;
public static final int TEACHER = 1;
private int type;
public void setType(@YuanZhen int type) {
this.type = type;
}
public int getType() {
return type;
}
}
使用时:
@StringDef和@IntDef的用法一样,这里就不再举例了。
注解允许我们在使用时传递参数,可以传递一个参数或者多个参数
当只有一个参数需要传递时,我们可以这样写:
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface YuanZhen {
String value() default "111";
}
@YuanZhen("222")
public void testMethod(){
}
//因为有默认值 也可以这样写
@YuanZhen()
public void testMethod(){
}
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface YuanZhen {
String value() default "111";
int age();
}
@YuanZhen(value = "222",age = 88)
public void testMethod(){
}
关于注解的基本知识就是这些,基本的注解并不难掌握,但是后面想要提高自己的架构能力,就必须结合APT,JavaPoet以及字节码插桩技术和反射,综合运用到框架中去,这并不是一件简单的事情,但是了解之后,我们就会发现一片新的天地。