Android Annotation 最全面直白的解析

初级程序员使用别人的框架,中级程序员不仅会使用别人的框架还知道内部的实现原理,高级程序员则按需求编写自己的框架。而在Android 开发中,很多常用的第三方库都会使用到注解来实现,比如 ButterKnife、EventBus 等,通过一个标注来说明当前类/方法/变量的意义或是赋值,从而使得代码的可读性变强。

那么今天我们就来聊聊这些主流框架中不可或缺的基石(休斯敦MVP-哈基石?)注解——Annotation吧~


注解基础

Annotation作为元数据可以被添加到Java源代码类、方法、变量、参数、包中。注解是代码里特殊的标记,这些标记可以在编译、类加载、运行时被读取,并执行相应处理。

标准注解

那么到底什么是Annotation呢,举几个大家熟悉的例子,如@Override(表示重载)、@Deprecated(表示下述方法不推荐使用,通常标注为此会提供推荐方法)等,这些在Java的JDK中内置的系统自带的注解,也常被称为标准注解

我们以 @Override来举个例子,大家都知道 @Override作用于方法,表示被标注的方法重载了父类的方法。若该重载的方法写错了比如方法名,那么在编译期就会出现警告以导致编译无法通过。它的写法其实很简单,如下:

package java.lang;

import java.lang.annotation.*;

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

简单的几行代码,初学者也许看得有点懵,没关系,接着往下看就能明白了。

元注解

当我们自己编写注解时,就要用到Java提供的专门用于标记注解的——元注解了。

常见的元注解有:@Target、@Retention、@Documented、@Inherited

@Target

Indicates the contexts in which an annotation type is applicable.

指定注解的目标,给什么类型注解。

参数如下:

ElementType.ANNOTATION_TYPE 
–can be applied to an annotation type.给注解注解
ElementType.CONSTRUCTOR 
–can be applied to a constructor.给构造方法注解
ElementType.FIELD 
–can be applied to a field or property. 给字段注解
ElementType.LOCAL_VARIABLE 
–can be applied to a local variable. 给局部变量注解
ElementType.METHOD 
–can be applied to a method-level annotation.给方法注解
ElementType.PACKAGE 
–can be applied to a package declaration.给包注解
ElementType.PARAMETER 
–can be applied to the parameters of a method.给参数注解
ElementType.TYPE 
–can be applied to any element of a class.   给类(型)注解
ElementType 常量在 Target 注解中至多只能出现一次,如下是非法的:@Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})


@Retention

Indicates how long annotations with the annotated type are to be retained.

指示该注解的保留时间,即表明注解的生存周期。

RetentionPolicy.SOURCE 
The marked annotation is retained only in the source level and is ignored by the compiler.
该注解只保留到代码层,编译器将对其忽略。因此其不会出现在生成的class文件中。

RetentionPolicy.CLASS
The marked annotation is retained by the compiler at compile time, but is ignored by the Java Virtual Machine (JVM).
该注解保留在编译器中,因此能出现在生成的class文件中,但是被JVM忽略,所以不能在运行时获取注解内容。

RetentionPolicy.RUNTIME
The marked annotation is retained by the JVM so it can be used by the runtime environment.
该注解能保留在JVM中,可以在运行时通过反射的方法获取具体内容。

如果自定义注解时不进行指定,默认为RetentionPolicy.CLASS。
 

@Documented

Indicates that annotations with a type are to be documented by javadoc and similar tools by default.

表示在生成javadoc文档时将该Annotation也写入到帮助文档。

 

@Inherited

Indicates that an annotation type is automatically inherited.

指示注解类型被自动继承。

如果在解析注解时发现了该字段,并且在该类中没有该类型的注解,则对其父类进行查询。举个例子,如果一个类中,没有A注解,但是其父类是有A注解的,并且A注解是被@Inherited注解的(不妨认为保留时态是Runtime),那么使用反射获取子类的A注解时,因为获取不到,所以会去其父类查询到A注解。使用了@Inherited注解的类,这个注解是可以被用于其子类。


自定义注解

知道了元注解后,我们来简单编写个自定义注解吧。自定义注解和创建接口非常相似,但注解需要以@开头。方法体中的每一个方法实际上是声明了一个属性,其中方法名是属性的名称,方法的返回值类型就是属性的类型(返回值类型只能是基本类型、String、enum、Class)。当然也可以通过default来声明属性的默认值。 比如:

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationSheep {
    String value();
    public String mvp() default "Harden";
    public int age() default 29;
}

我们自定义了一个注解 AnnotationSheep,它有三个属性 value、mvp和age,并且mvp和age分别有默认值 "Harden" 和29。(有时注解中只需要一个属性,为简便起见可将该属性命名为value)

下面我们为一个测试类添加上面的注解,并在该类的方法中将注解的属性值取出来:

@AnnotationSheep(value = "haha",age = 30)
public class AnnotationTest{

    getAnnotationValues(){
        AnnotationSheep annotationSheep=null;
        Class mClass=getClass();
        boolean isAnnotationPresent=mClass.isAnnotationPresent(AnnotationSheep.class);
        if(isAnnotationPresent){
            annotationSheep= (AnnotationSheep) mClass.getAnnotation(AnnotationSheep.class);
            String value=annotationSheep.value();
            String mvp=annotationSheep.mvp();
            int age=annotationSheep.age();
            Log.e("sheep","value="+value+",mvp="+mvp+",age="+age);
        }
    }
}

可以看到,先判断注解是否存在,然后获得注解,继而获取注解的属性值,输出结果如下:

E/sheep: value=haha,mvp=Harden,age=30


注入框架的原理

ButterKnife这个开源库大家一定很多人都用过,下面我们就通过一个demo来演示一下这些库的原理,其实还蛮简单的。

@Target({java.lang.annotation.ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
    public int value();
}

@Target({java.lang.annotation.ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewOnClick
{
    public int[] value();
}

然后定义一个解析器,在 Activity中调用即可。

public static void inject(final Activity activity) {

        Class clazz = activity.getClass();
        Field[] declaredFields = clazz.getDeclaredFields();
        //遍历该activity中所有的field。
        for (int i = 0; i < declaredFields.length; i++) {
            Field field = declaredFields[i];
            field.setAccessible(true);
            //获取到字段上面的注解对象
            ViewInject annotation = (ViewInject) field.getAnnotation(ViewInject.class);
            if (annotation == null) {
                continue;
            }
            //获取注解中的值
            int id = annotation.value();
            //获取控件
            View view = activity.findViewById(id);

            try {
                //将该控件设置给field对象
                field.set(activity, view);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }

        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (int i = 0; i < declaredMethods.length; i++) {
            final Method method = declaredMethods[i];
            //获取方法上面的注解
            ViewOnClick annotation = (ViewOnClick) method.getAnnotation(ViewOnClick.class);
            if (annotation == null) {
                continue;
            }
            //获取注解中的数据,可以给多个button绑定点击事件
            int[] value = annotation.value();
            for (int j = 0; j < value.length; j++) {
                int id = value[j];

                final View button = activity.findViewById(id);

                button.setOnClickListener(new View.OnClickListener() {
                    public void onClick(View v) {
                        try {
                            method.invoke(activity, button);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
}

至此,相信大家对 Annotation 都昭昭在目了吧~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值