注解
- 更通俗的意思是为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且是供指定的工具或框架使用的。
- Annotation其实是一种接口。通过反射来访问annotation信息。相关类(框架或工具中的类)根据这些信息来决定如何使用该程序元素或改变它们的行为。
- Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
Java注解
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
Java内置了三种标准注解,其定义在 java.lang 中。
- @Override,表示当前的方法定义将覆盖超类中的方法。
- @Deprecated,被此注解标记的元素表示被废弃,如果程序员使用了注解为它的元素,那么编译器会发出警告。
- @SuppressWarnings,关闭不当的编译器警告信息。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
那注解的注解又是啥呢,这里就要引出接下来要说的,元注解,专职负责注解其他的注解。
四种元注解,定义在 java.lang.annotation 中
- @Target 注解所修饰的对象范围
- @Retention 表示需要在什么级别保存该注解信息(字节流/class文件/虚拟机)
- @Documented 表示将此注解应该被Javadoc工具记录
- @Inherited 表示允许子类继承父类中的注解
Android注解
有两个原生注解。位于android.annotation包中
- @TargetApi 使高版本API的代码在低版本SDK不报错。
- @SuppressLint 使用此标注让Lint忽略指定的警告。
@SuppressLint(“HandlerLeak”) 在主线程用Handler处理消息出现时会有警告,提示你,这块有内存泄露的危险,handler最好声明为static的
注解处理器
- 运行时注解
会采用反射机制处理。 - 编译时注解
会采用AbstractProcessor处理,生成$$_ViewBinder类。
注解好处
首先,由于注解分了三个阶段,有源码阶段、编译阶段、运行阶段,不同阶段的注解有不同作用。
-
编译前提示信息:注解可以被编译器用来发现错误,或者清除不必要的警告;而且还可以利用APT技术进行自动化编码。
-
编译时生成代码:一些处理器可以在编译时根据注解信息生成代码,比如 Java 代码,xml 代码等;
-
运行时处理:我们可以在运行时根据注解,通过反射获取具体信息,然后做一些操作。反射必须在软件运行后才能反射。这样就使得编程更加简洁,代码更加清晰。
-
能够帮忙查看代码 通过 @Override, @Deprecated 等,我们能很方便的了解程序的大致结构。
Butterknife
ButterKnife是一个专注于Android系统的View注入框架,可以轻松代替findViewById找到View对象的方式。最重要的一点,使用ButterKnife对性能基本没有损失,因为ButterKnife用到的注解并不是在运行时反射的,而是在编译的时候生成新的class。
-
ButterKnifeProcessor(注解处理器)会生成$$_ViewBinder类并实现了ViewBinder接口。
-
$$_ViewBinder类中包含了所有对应的代码,会通过注解去解析到id等,然后通过findViewById()等方法找到对应的控件,并且复制给调用该方法的来中的变量。这样就等同于我们直接 使用View v = findViewByid(R.id.xx)来进行初始化控件。
-
上面虽然生成了$ V i e w B i n d e r 类,但是如何去调用呢?就是在调用 B u t t e r K n i f e . b i n d ( t h i s ) 时执行,该方法会通过反射去实例化对应的 _ViewBinder类,但是如何去调用呢? 就是在调用ButterKnife.bind(this)时执行,该方法会通过反射去实例化对应的 ViewBinder类,但是如何去调用呢?就是在调用ButterKnife.bind(this)时执行,该方法会通过反射去实例化对应的$_ViewBinder类,并且调用该类的bind()方法。
-
Butterknife除了在Butterknife.bind()方法中使用反射之外,其他注解的处理都是通过编译时注解使用,所以不会影响效率。
编译时注解的使用一般分为三步:
- 用注解修饰变量
- 编译时使用注解处理器生成代码
- 行时调用生成的代码
编写编译时注解项目的步骤:
- 先创建注解
- 创建注解处理器,在其中拿到注解修饰的变量信息,生成需要的代码
- 创建运行时,调用生成代码的调度器
ava代码在计算机中的三个阶段
源代码阶段 - 类对象阶段 - 运行阶段(图片来源于网络)