在看这篇文章之前对于不知道什么是注解的建议先看上一篇《JAVA注解》 穿越门 ,如果知道的话就可以跳过了。
一. 概述
首先在讲运行时注解之前,有必要先说一下注解其存在周期。对于JAVA自定义注解其存在的周期主要和其元注解
@Retention
复制代码
的赋值有关。
元注解的赋值一共有如下三种:
- RetentionPolicy.SOURCE( 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。)
- RetentionPolicy.CLASS (注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。 )
- RetentionPolicy.RUNTIME (注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。)
这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码
我们的运行时注解对应标注为
@RetentionPolicy.RUNTIME
复制代码
的注解,即代码在内存中运行时可获取处理的注解。
二.定义添加注解
2.1 自定义注解
本次自定义注解目标为实现一个汽车类信息注解(CarInfo),该注解作用于Car类的值上。可实现不使用set方法,通过注解给该值添加对象并完成初始化的功能。
实现该注解第一步需在annotation包下自定义一个***CarInfo***的注解,代码如下
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CarInfo {
String name () default "" ;
int size () default 0 ;
}
复制代码
从元注解的信息可知,该注解可作用于值,生命周期到运行时一直存在,该注解主要包括车名和车数量两个内容。
2.2 添加注解
在MainActivity类中定义一个Car类的值,然后在他上面添加CarInfo注解,并添加name和size相关属性。添加完该注解以后,我们在代码运行时就能获取car值上的注解内容了。
@CarInfo(name = "BMW",size = 100)
Car car;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//这里我们要首先注册一下这个类
AnnotationCar.instance().inject(this);
//当程序运行的时候这里将会输出该类Car的属性值。
Log.e("WANG","Car is "+car.toString());
}
复制代码
三.处理注解
运行时注解核心的内容就是注解处理。 注解处理有两个核心问题:
- 如何通过代码去获取注解?
- 什么时候去获取注解?
对于问题一,答案就是通过反射去获取注解。 思路大概如下:
- 根据传入的对象获取到该对象对应的类。
- 通过该类获取到类中的所有值
- 从这些值中筛选添加了CarInfo注解的值
- 获取到这些值上的注解,根据注解内容,给该对象初始化赋值。
对于问题二,具体问题需要具体分析,不过一般可把时机放早点。我放在onCreate方法内。
no code no truth
注解处理相关代码
public class AnnotationCar {
private static AnnotationCar annotationCar;
public static AnnotationCar instance(){
synchronized (AnnotationCar.class){
if(annotationCar == null){
annotationCar = new AnnotationCar();
}
return annotationCar;
}
}
public void inject(Object o){
Class<?> aClass = o.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field:declaredFields) {
if(field.getName().equals("car") && field.isAnnotationPresent(CarInfo.class)) {
CarInfo annotation = field.getAnnotation(CarInfo.class);
Class<?> type = field.getType();
if(MainActivity.Car.class.equals(type)) {
try {
field.setAccessible(true);
field.set(o, new MainActivity.Car(annotation.name(), annotation.size()));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}
复制代码
注解注入过程
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//这里我们要首先注册一下这个类
AnnotationCar.instance().inject(this);
//当程序运行的时候这里将会输出该类Car的属性值。
Log.e("WANG","Car is "+car.toString());
}
复制代码
运行结果
2018-12-25 17:07:09.935 12026-12026/android.weifeng.com.annotationtest E/WANG: Car is Car{name='BMW', size=100}
复制代码
获取到了注解中的内容,注解成功。
四.思考
- 运行时处理注解内容过程需要大量用到Java反射,性能必然受影响。
- 运行时注解注入放到onCreate中,会影响该Activity的启动时间,但是许多情况需要又不得不放在onCreate中处理。
- 针对上面的问题,可以使用编译时注解来代替运行时注解。编译时注解是在代码编译阶段就生成了相应的代码,不存在大量反射的问题,同时在onCreate中代码运行时间也可以缩短。