您的当前位置:首页Android编译时注解框架系列1

Android编译时注解框架系列1

2024-12-14 来源:哗拓教育

Java注解(annotation)

什么是注解?

定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

Annotation是被动的元数据,永远不会有主动行为
既然是被动数据,对于那些已经存在的注解,比如Override,我们只能看看而已,并不知道它具体的工作机制是什么;所以想要理解注解,就直接从自定义注解开始。

自定义注解

下面是一段代码示例,具体每个代码功能下面会讲到:

    //用来标记自定义注解的有效范围
    @Retention(RetentionPolicy.RUNTIME)
    //指定Annotation用于修饰哪些程序元素
    @Target({ElementType.TYPE})
    //用于描述其它类型的annotation应该被作为被标注的程序成员的公共API
    @Documented
    //元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的
    @Inherited
    public @interface ContentView {//@interface表明这是一个注解
        int value() default -1;
    }

元注解:
  元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:

1.@Target,    
2.@Retention,    
3.@Documented,    
4.@Inherited  

这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和相应分参数的使用说明。

@Target:
   @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
  作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
  取值(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)有:
    
1.SOURCE:在源文件中有效(即源文件保留)    
2.CLASS:在class文件中有效(即class保留)    
3.RUNTIME:在运行时有效(即运行时保留)

Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。

@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类型被发现,或者到达类继承结构的顶层。

自定义注解:
  使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
  定义注解格式:  public @interface 注解名 {定义体}
  ****注解参数的可支持数据类型:

1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)    2.String类型    
3.Class类型    
4.enum类型    
5.Annotation类型    
6.以上所有类型的数组

Annotation类型里面的参数该怎么设定:

第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;     
第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;    
第三,如果只有一个参数成员,最好把参数名称设为"value",后加小括号.

     //注解解析
    //一下步骤可以在base基类完成
    //        for (Class c = this.getClass(); c != Context.class; c = c.getSuperclass()) {
                ContentView annotation = this.getClass().getAnnotation(ContentView.class);
                if (annotation != null) {
                    try {
                        this.setContentView(annotation.value());
                    } catch (RuntimeException e) {
                        e.printStackTrace();
                    }
                    return;
    //            }
            }

第一步:遍历所有的子类
第二步:找到修饰了注解ContentView的类
第三步:获取ContentView的属性值。
第四步:为Activity设置布局。

总结
你可能会觉得setContentView(R.layout.activity_home)@ContentView(R.layout.activity_home)没什么区别,用了注解反而还增加了性能问题。

但你要知道,这只是注解最简单的应用方式。举一个例子:AndroidEventBus的注解是运行时注解,虽然会有一点性能问题,但是在开发效率上是有提高的。

注意

很多人会问,在java代码中利用反射会不会对app性能产生影响,答案是肯定的,但是有解决的办法呢,就是这个 Java Annotation Processing

Annotation processing 是javac中用于编译时扫描和解析Java注解的工具。Annotation processing是在编译阶段执行的,它的原理就是读入Java源代码,解析注解,然后生成新的Java代码。新生成的Java代码最后被编译成Java字节码,注解解析器(Annotation Processor)不能改变读入的Java 类,比如不能加入或删除Java方法

这样性能的问题就解决了呢

显示全文