转载请注明出处:http://blog.csdn.net/binbinqq86/article/details/79610980
注解是从Java1.5开始引入的,当前许多java框架中大量使用注解,如Hibernate、Jersey、Spring。而在Android中,大名鼎鼎的Retrofit,ButterKnife都使用了注解的方式。我们在写Android页面的时候,经常会findViewById来绑定控件,当一个页面非常复杂的时候,这个工作就是个体力活了,于是ButterKnife就应运而生了,类似的还有之前的XUtils等,但是目前使用的最广泛的还是ButterKnife,用法简单,功能完善,持续维护。而这仅仅是在绑定控件领域的注解框架,另外还有EventBus方便我们实现组建间通讯,ParcelableGenerator可实现自动将任意对象转换为Parcelable类型,方便对象传输,等等,各种功能强大的注解都有。
虽然有了轮子,但是我们还是想造一个自己的轮子,今天就带大家一步一步打造一个属于自己的注解框架,其实明白了原理,各种功能强大的注解对我们来说都轻而易举可以实现了。我们就从findViewById入手来开始今天的讲解~
一、注解的分类
首先就是看一下Java中注解的分类,其实是元注解的分类,也就是用来注解自定义注解的原始注解。包括@Retention、@Target、@Document、@Inherited四种。
- @Documented —— 指明拥有这个注解的元素可以被javadoc此类的工具文档化。这种类型应该用于注解那些影响客户使用带注释的元素声明的类型。如果一种声明使用Documented进行注解,这种类型的注解被作为被标注的程序成员的公共API。
- @Target——指明该类型的注解可以注解的程序元素的范围。该元注解的取值可以为TYPE,METHOD,CONSTRUCTOR,FIELD等。如果Target元注解没有出现,那么定义的注解可以应用于程序的任何元素。
- @Inherited——指明该注解类型被自动继承。如果用户在当前类中查询这个元注解类型并且当前类的声明中不包含这个元注解类型,那么也将自动查询当前类的父类是否存在Inherited元注解,这个动作将被重复执行直到这个标注类型被找到,或者是查询到顶层的父类。
- @Retention——指明了该Annotation被保留的时间长短。RetentionPolicy取值为SOURCE,CLASS,RUNTIME。
- @Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
- @Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
- @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
二、注解的一些特性
- 注解方法不能带有参数;
- 注解方法返回值类型限定为:基本类型、String、Enums、Annotation或者是这些类型的数组;
- 注解方法可以有默认值;
- 注解本身能够包含元注解,元注解被用来注解其它注解。
三、内建注解
Java提供了三种内建注解。
@Override——当我们想要复写父类中的方法时,我们需要使用该注解去告知编译器我们想要复写这个方法。这样一来当父类中的方法移除或者发生更改时编译器将提示错误信息。
@Deprecated——当我们希望编译器知道某一方法不建议使用时,我们应该使用这个注解。Java在javadoc 中推荐使用该注解,我们应该提供为什么该方法不推荐使用以及替代的方法。
@SuppressWarnings——这个仅仅是告诉编译器忽略特定的警告信息,例如在泛型中使用原生数据类型。它的保留策略是SOURCE(译者注:在源文件中有效)并且被编译器丢弃。
而Android内建注解就比较多了,@Keep、@NonNull、@Nullable、@StringRes等等等等,非常之多,他们都在谷歌提供的support-annotations这个库里。
我们可以看到,一共有这么多的注解。说了这么多,怎么去实现我们自己的注解呢,类似ButterKnife这种来替代重复的体力劳动的,下面就来开始打造我们自己的注解。
四、打造自己的注解(运行时注解)
这里我们先讲一下运行时的注解,编译时注解我们待下回分解~老规矩,上代码:
/**
* @auther tb
* @time 2018/3/20 下午2:09
* @desc 绑定view的id或者layout的id,包含findViewById和setContentView两个功能
*/
@Retention(RUNTIME)
@Target({FIELD, TYPE})
public @interface BindId {
int value() default View.NO_ID;
}
上面