Android-Annotation教你写自定义注解

一 前言

我相信注解咱们多多少少的都会接触到,经常使用的框架Butterknife、Retrofit、ARouter等等都用到了注解,我想你们都会去搜一下什么是注解了吧。这里呢就以一个Demo去了解一下自定义注解的使用。java

二 知识准备

咱们最多见的注解莫过于@Override了吧,那咱们就去看一下这个注解的代码。git

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}复制代码

好了就事论事咱们先看下声明这个@interface乍一看还觉得是interface呢不过这里多了个@那就是声明注解的关键字了,这只要记住就行了。大括号里面居然没任何的定义,那就先放一放,咱们来看一下其余的参数。github

  • @Target()
    @Target说明了Annotation所修饰的对象范围,也就是咱们这个注解是用在那个对象上面的:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
    做用:用于描述注解的使用范围(即:被描述的注解能够用在什么地方)取值(ElementType)是来源于Java.lang.annotation.ElementType中的枚举类型元素:
       (1).CONSTRUCTOR:用于描述构造器
       (2).FIELD:用于描述域
       (3).LOCAL_VARIABLE:用于描述局部变量
       (4).METHOD:用于描述方法
       (5).PACKAGE:用于描述包
       (6).PARAMETER:用于描述参数
       (7).TYPE:用于描述类、接口(包括注解类型) 或enum声明
  • @Retention()
    @Retention定义了该Annotation被保留的时间长短:某些Annotation仅出如今源代码中,而被编译器丢弃;而另外一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另外一些在class被装载时将被读取(请注意并不影响class的执行,由于Annotation与class在使用上是被分离的)。使用这个meta-Annotation能够对 Annotation的“生命周期”限制。
    做用:表示须要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)取值(RetentionPoicy)来源于java.lang.annotation.RetentionPolicy的枚举类型值:
       (1).SOURCE:在源文件中有效(即源文件保留)
       (2).CLASS:在class文件中有效(即class保留)
       (3).RUNTIME:在运行时有效(即运行时保留)
  • @Documented
    @Documented用于描述其它类型的annotation应该被做为被标注的程序成员的公共API,所以能够被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
  • @Inherited:
    @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。若是一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。 注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API加强了这种继承性。若是咱们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工做:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
这里呢大概的讲了一下定义在类上面的每一个注解的意义和取值是什么,下面咱们将进入咱们的自定义注解中。

三 自定义注解

咱们先来照葫芦画瓢,定义一个注解类数组

public @interface MyTag {

}复制代码

注解里面的定义也是有规定的:bash

  • 注解方法不能带有参数。markdown

    • 注解方法返回值类型限定为:基本类型、String、Enums、Annotation或者这些类型的数组。
  • 注解方法能够有默认值。框架

  • 注解自己可以包含元注解,元注解被用来注解其余注解。ide

咱们就来试一下吧!工具

public @interface MyTag {
  //声明返回值类型,这里可没有大括号啊,能够设置默认返回值,而后就直接";"了啊。
    String name () default "" ;

    int size () default 0 ;

}复制代码

定义好了注解咱们就来规定咱们这个注解要用到哪里什么时候用吧!oop

@Target({ElementType.METHOD,ElementType.FIELD})
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTag {
    String name () default "" ;
    int size () default 0 ;
}复制代码

这里呢咱们定义这个注解能够用在属性和方法上面,是可继承的注解,能够出如今运行时的。由于咱们这边要模仿一下一下其余注解框架中注解的用法,我这里才采用了RetentionPolicy.RUNTIME,由于在运行时咱们采用反射能够获得里面的注解信息。
好了接下来看怎么使用咱们的这个自定义的注解!

public class HomeActivity extends AppCompatActivity {
    @MyTag(name = "BMW",size = 100)
    Car car;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        //这里咱们要首先注册一下这个类
        AnnotationCar.instance().inject(this);
      //当程序运行的时候这里将会输出该类Car的属性值。
        Log.e("WANG","Car is "+car.toString());
    }
}复制代码

是否是很像咱们使用过的ButterKnife呢,这里呢咱们再这个Activity里面定义了一个Car类的属性,而后再car这个变量上面定义咱们的注解,而且给咱们的注解赋值。而后咱们再onCreate方法里面先初始化咱们的注解,而后打印Car类的信息,先来看下结果吧

cn.example.wang.routerdemo E/WANG: Car is Car [name=BMW, size=100]复制代码

这样咱们的自定义注解就有做用了,好了半天主要的代码就在那个初始化里面。

//自定义的类
/**
 * Created by WANG on 17/11/21.
 */

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(MyTag.class)) {
                MyTag annotation = field.getAnnotation(MyTag.class);
                Class<?> type = field.getType();
                if(Car.class.equals(type)) {
                    try {
                        field.setAccessible(true);
                        field.set(o, new Car(annotation.name(), annotation.size()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}复制代码

这就说明了为何注解和反射是同时进入咱们的知识圈里面的吧!这里呢咱们先获取到类里面全部的属性,而后去找到被咱们的注解MyTag修饰的那个属性,而后找到以后,先取咱们注解里面的值,而后赋值给咱们类里面的属性!这样咱们就用注解去初始化了一个属性值,嘻嘻这里就结束了啊!

结束语

例子很简单,看完以后是否是也会写一个相似ButterKnife的效果了呢,有什么问题请留言给我,我会实时为你解答的!

感受对你有帮助请点个赞啊~
欢迎关注 个人掘金
欢迎关注 个人简书
欢迎关注 个人csdn
源码下载

相关文章
相关标签/搜索