Android 注解(Annotation)的入门与使用(一)
概述
现在很多流行的框架都用到了注解,比如Butterknife、EventBus3、Retrofit,Okhttp里面也用到了注解,减少了重复代码的编写,极大的方便我们快速开发,也是一直好奇他们都是怎么做到的,注解的工作原理是啥。咱们能不能自己去实现一个简单的注解呢?
什么是注解(Annotation)
注解(Annotation)是JDK1.5新增加功能,注解其实就是添加在类、变量、方法、参数等前面的一个修饰符,并且可以在某个生命周期中(java源码中,编译期,Runtime)被反射获取。Annotation并不是直接影响它所注解的代码,例如:@Override就是注解,检查是否正确的重写了父类中的方法
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
注解(Annotation)用来做什么
- 给编译器提供信息–例如提供给编译器探测错误和压制警告等等 :
- 编译期生成代码 :
- 运行期(Runtime)处理注解 :
- 简化代码,增加阅读性,提高代码复用率,并提高开发效率,但不一定能提高运行效率哦 :
元注解
在我们定义注解之前让我们先来了解一下元注解,annotation提供了五种元注解:@Retention、 @Target、@Inherited、@Documented、@Repeatable
- @Retention :注解的生命周期,就是注解存活的时间
- @Target :表示注解可以出现的地方,作用于什么地方,如类,方法,变量等等
- @Inherited :允许子类继承父类中的注解,默认为false
- @Documented :代表着此注解的元素会被javadoc工具提取成文档
- @Repeatable : Java SE8引入的注解,表示这个注解可以在同一处多次声明
@Target有哪些值
@Target-ElementType类型 | 说明 |
---|---|
ElementType.ANNOTATION_TYPE | 可以使用在注解类型上 |
ElementType.CONSTRUCTOR | 可以使用在构造方法上 |
ElementType.FIELD | 可以使用在属性(成员变量)上 |
ElementType.LOCAL_VARIABLE | 可以使用在局部变量上 |
ElementType.METHOD | 可以使用在方法上 |
ElementType.PACKAGE | 可以使用在包声明上 |
ElementType.PARAMETER | 可以使用在方法参数上 |
ElementType.TYPE | 可以使用在类中任何元素 |
@Retention有哪些值
@Retention-RetentionPolicy | 说明 |
---|---|
RetentionPolicy.SOURCE | 存在java源码中,当Java文件编译成class文件的时候,注解被遗弃 |
RetentionPolicy.CLASS | 存活到编译成Class中,但jvm加载class文件时候被遗弃,这是默认的生命周期 |
RetentionPolicy.RUNTIME | 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在 |
自定义注解
了解了元注解后,看看如何实现和使用自定义注解,自定义注解一般分为两步,一个是定义注解、第二个是解析注解,
定义注解 :
1.首先声明@Retention确定我们的注解是在什么时候使用
2.@Target确定我们注解是作用在什么上面的(变量、函数、类等)。
3.确定我们需要的参数
比如下面我们定义了一个作用在方法上的PermissionSucceed运行时注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionSucceed {
int requestCode();
}
解析注解 :
1.找到类对应的所有属性或者方法,主要看Target作用在什么地方
2.找到添加了我们注解的属性或者方法。
3.做我们注解需要自定义的一些操作。
获取类的属性和方法
既然是自定义注解我们肯定会首先确定我们的注解是作用于类,方法,还是变量上,然后通过Class对象我们就可以获取到,这里介绍一下Class对象中的一些方法,类里面每个属性对应一个对象Field,每个方法对应一个对象Method
/**
* 包名加类名
*/
public String getName();
/**
* 类名
*/
public String getSimpleName();
/**
* 返回当前类和父类层次的public构造方法
*/
public Constructor<?>[] getConstructors();
/**
* 返回当前类所有的构造方法(public、private和protected)
* 不包括父类
*/
public Constructor<?>[] getDeclaredConstructors();
/**
* 返回当前类所有public的字段,包括父类
*/
public Field[] getFields();
/**
* 返回当前类所有申明的字段,即包括public、private和protected,
* 不包括父类
*/
public native Field[] getDeclaredFields();
/**
* 返回当前类所有public的方法,包括父类
*/
public Method[] getMethods();
/**
* 返回当前类所有的方法,即包括public、private和protected,
* 不包括父类
*/
public Method[] getDeclaredMethods();
/**
* 获取局部或匿名内部类在定义时所在的方法
*/
public Method getEnclosingMethod();
/**
* 获取当前类的包
*/
public Package getPackage();
/**
* 获取当前类的包名
*/
public String getPackageName$();
/**
* 获取当前类的直接超类的 Type
*/
public Type getGenericSuperclass();
/**
* 返回当前类直接实现的接口.不包含泛型参数信息
*/
public Class<?>[] getInterfaces();
/**
* 返回当前类的修饰符,public,private,protected
*/
public int getModifiers();
获取注解的属性或者方法
获取注解的属性或者方法我们通过AnnotatedElement接口来获取,File和Method都实现了该接口
/**
* 指定类型的注释是否存在于此元素上
*/
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
/**
* 返回该元素上存在的指定类型的注解
*/
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
/**
* 返回该元素上存在的所有注解
*/
Annotation[] getAnnotations();
/**
* 返回该元素指定类型的注解
*/
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
return AnnotatedElements.getDirectOrIndirectAnnotationsByType(this, annotationClass);
}
/**
* 返回直接存在与该元素上的所有注释(父类里面的不算)
*/
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
// Loop over all directly-present annotations looking for a matching one
for (Annotation annotation : getDeclaredAnnotations()) {
if (annotationClass.equals(annotation.annotationType())) {
// More robust to do a dynamic cast at runtime instead
// of compile-time only.
return annotationClass.cast(annotation);
}
}
return null;
}
/**
* 返回直接存在该元素岸上某类型的注释
*/
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
return AnnotatedElements.getDirectOrIndirectAnnotationsByType(this, annotationClass);
}
/**
* 返回直接存在与该元素上的所有注释
*/
Annotation[] getDeclaredAnnotations();
实战如何使用自定义注解执行一个方法
上面我们已经知道了如何获取我们自定义注解中的属性或者方法,接下来我们就来实战一下,通过定义一个注解来自动调用一个方法
@Target(ElementType.TYPE)//用于描述类
@Retention(RetentionPolicy.RUNTIME) //运行时注解
public @interface ExcuteMethod {
}
定义了一个InjectUtils 工具类,把Class对象传进来,然后通过遍历,获取,执行等操作
public class InjectUtils {
public static void injectExcuteMethod(Activity activity){
Class aClass = activity.getClass();
//获取该类的所有方法,包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段
Method[] methods = aClass.getDeclaredMethods();
for (Method method:methods){
//判断当前类是否有ExcuteMethod注解
if(method.isAnnotationPresent(ExcuteMethod.class)){
//获取注解实列
ExcuteMethod findView = method.getAnnotation(ExcuteMethod.class);
try {
//设置访问权限
method.setAccessible(true);
//反射执行该方法
method.invoke(activity, null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
我们来看看如何使用
public class Test1 extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.permissions_test1);
InjectUtils.injectExcuteMethod(this);
}
@ExcuteMethod
public void test(){
Toast.makeText(Test1.this,"执行了",1000).show();
}
}
实战如何使用自定义注解执行setContentView
定义ContentView注解首先需要一个参数,而且规定需要一个int参数。int参数代表View对应的layout
@Target(ElementType.TYPE)//用于描述类
@Retention(RetentionPolicy.RUNTIME) //运行时注解
public @interface ContentView {
int value();
}
public class InjectUtils {
public static void injectContentView(Activity activity){
Class aClass = activity.getClass();
//判断当前类是否有ContentView注解
if(aClass.isAnnotationPresent(ContentView.class)){
//获取注解实列
ContentView contentView = (ContentView) aClass.getAnnotation(ContentView.class);
//获取注解里面的值
int value = contentView.value();
//获取class的方法,第一个参数是方法名,第二个是方法参数的类型
try {
Method method = aClass.getMethod("setContentView", int.class);
method.setAccessible(true);
//调用指定对象的此方法,第二个是方法的参数
try {
method.invoke(activity,value);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
}
@ContentView(R.layout.permissions_test1)
public class Test1 extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectUtils.injectContentView(this);
}
}