Android 注解(Annotation)的入门与使用(一)

概述

现在很多流行的框架都用到了注解,比如Butterknife、EventBus3、Retrofit,Okhttp里面也用到了注解,减少了重复代码的编写,极大的方便我们快速开发,也是一直好奇他们都是怎么做到的,注解的工作原理是啥。咱们能不能自己去实现一个简单的注解呢?

什么是注解(Annotation)

注解(Annotation)是JDK1.5新增加功能,注解其实就是添加在类、变量、方法、参数等前面的一个修饰符,并且可以在某个生命周期中(java源码中,编译期,Runtime)被反射获取。Annotation并不是直接影响它所注解的代码,例如:@Override就是注解,检查是否正确的重写了父类中的方法

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

注解(Annotation)用来做什么

  1. 给编译器提供信息–例如提供给编译器探测错误和压制警告等等
  2. 编译期生成代码
  3. 运行期(Runtime)处理注解
  4. 简化代码,增加阅读性,提高代码复用率,并提高开发效率,但不一定能提高运行效率哦

元注解

在我们定义注解之前让我们先来了解一下元注解,annotation提供了五种元注解:@Retention、 @Target、@Inherited、@Documented、@Repeatable

  1. @Retention :注解的生命周期,就是注解存活的时间
  2. @Target :表示注解可以出现的地方,作用于什么地方,如类,方法,变量等等
  3. @Inherited :允许子类继承父类中的注解,默认为false
  4. @Documented :代表着此注解的元素会被javadoc工具提取成文档
  5. @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);
        
    }
    
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值