Android注解学习笔记

来源:互联网 发布:数据库设计与开发 下载 编辑:程序博客网 时间:2024/06/05 06:33

本文学习自《Android注解快速入门和实用解析》。

元注解

java提供的基础注解,用来注解其他自定义注解,解释说明作用,位于sdk/sources/android-25/java/lang/annotation目录下

元注解有:

  • @Retention:注解保留的生命周期

  • @Target:注解对象的作用范围

  • @Inherited:标明的注解,在其作用的类上,能否被继承

  • @Documented:javadoc的工具文档化

@Retention

说明了注解的生命周期,对应RetentionPolicy枚举,标明注解何时生效。

  • SOURCE:只在源码中有效,编译时被抛弃,如@override

  • CLASS:编译时生效

  • RUNTIME:运行时生效

@Target

标明了注解的作用范围,对应于ElementType枚举

TYPE FIELD METHOD PARAMETER CONSTRUCTOR LOCAL_VARIABLE ANNOTATION_TYPE PACKAGE TYPE_PARAMETER TYPE_USE

这里写图片描述

/**   * Nullable表明   * bind方法的参数target和返回值Data可以为null   */  @Nullable   public static Data bind(@Nullable Context target) {    //do someThing and return    return bindXXX(target);  }

@Inherited

子类默认无法继承父类的注解,Inherited注解可以使子类继承注释,但是只能作用在类上,无法作用在方法和变量上。

@Retention(RetentionPolicy.RUNTIME)  @Inherited  public @interface AInherited {      String value();  }  @Retention(RetentionPolicy.RUNTIME)  public @interface BNotInherited {      String value();  }  @AInherited("Inherited")  @BNotInherited("没Inherited")  public class Parent {      @AInherited("Inherited")      @BNotInherited("没Inherited")      public void testOverride(){      }      @AInherited("Inherited")      @BNotInherited("没Inherited")      public void testNotOverride(){    }}  /**  * Child继承了Parent的AInherited注解  * BNotInherited因为没有@Inherited声明,不能被继承  */public class Child extends Parent {    /**   * 重写的testOverride不继承任何注解   * 因为Inherited不作用在方法上   */    @Override      public void testOverride() {      }    /**   * testNotOverride没有被重写   * 所以注解AInherited和BNotInherited依然生效。   */}

自定义注解

运行时注解

首先,创建一个注解遵循: public @interface 注解名 {方法参数},如下方@getViewTo注解:

@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)public @interface getViewTo {    int value() default  -1;}

然后如下方所示,我们将注解描述在Activity的成员变量mTvmBtn中,在App运行时,通过反射将findViewbyId得到的控件,注入到mTvmBtn中。

    @getViewTo(R.id.textview)    private TextView mTv;    @getViewTo(R.id.button)    private Button mBtn;
    private void getAllAnnotationView() {        //获得成员变量        Field[] fields = this.getClass().getDeclaredFields();        for (Field field : fields) {          try {            //判断注解            if (field.getAnnotations() != null) {              //确定注解类型              if (field.isAnnotationPresent(GetViewTo.class)) {                //允许修改反射属性                field.setAccessible(true);                GetViewTo getViewTo = field.getAnnotation(GetViewTo.class);                //findViewById将注解的id,找到View注入成员变量中                field.set(this, findViewById(getViewTo.value()));              }            }          } catch (Exception e) {          }        }      }

编译时注解

注解处理器AbstractProcessor是一个javac的工具,用来在编译时扫描和处理,一般第三方注解相关的类库,都有一个Compiler命名的Module,这里面一般都是注解处理器。

@AutoService(Processor.class)public class CustomProcessor extends AbstractProcessor {    /**     * 注解处理器的初始化     * 一般在这里获取我们需要的工具类     * @param processingEnvironment 提供工具类Elements, Types和Filer     */    @Override    public synchronized void init(ProcessingEnvironment env){         super.init(env);        //Element代表程序的元素,例如包、类、方法。        mElementUtils = env.getElementUtils();        //处理TypeMirror的工具类,用于取类信息        mTypeUtils = env.getTypeUtils();         //Filer可以创建文件        mFiler = env.getFiler();        //错误处理工具        mMessages = env.getMessager();    }    /**     * 处理器实际处理逻辑入口     * @param set     * @param roundEnvironment 所有注解的集合     * @return      */    @Override    public boolean process(Set<? extends TypeElement> annoations,       RoundEnvironment env) {        //do someThing    }    //指定注解处理器是注册给哪个注解的,返回指定支持的注解类集合。    @Override    public Set<String> getSupportedAnnotationTypes() {           Set<String> sets = new LinkedHashSet<String>();          //大部分class而已getName、getCanonicalNam这两个方法没有什么不同的。          //但是对于array或内部类等就不一样了。          //getName返回的是[[Ljava.lang.String之类的表现形式,          //getCanonicalName返回的就是跟我们声明类似的形式。          sets(BindView.class.getCanonicalName());          return sets;    }    //指定Java版本,一般返回最新版本即可    @Override    public SourceVersion getSupportedSourceVersion() {        return SourceVersion.latestSupported();    }}

一般处理器处理逻辑:

  • 遍历得到源码中,需要解析的元素列表。

  • 判断元素是否可见和符合要求。

  • 组织数据结构得到输出类参数。

  • 输入生成java文件。

  • 错误处理。

Processor处理过程中会扫描源码,代码中的每一部分都是个特定类型的Element,就像XML文件里面的层级一样,类、变量、方法等都属于不同的Element层级。每个Element代表一个静态的、语言类级别的构件。

package android.demo; // PackageElement// TypeElementpublic class DemoClass {    // VariableElement    private boolean mVariableType;    // VariableElement    private VariableClassE m VariableClassE;    // ExecuteableElement    public DemoClass () {    }    // ExecuteableElement    public void resolveData (Demo data   //TypeElement ) {    }}

Element代表源码,TypeElement代表源码中的类型元素,TypeElement只能获取到相对应的元素,不能获得类的信息,可以通过element.asType()获取到TypeMirrorTypeMirror可以获取类信息。

知道了Element,我们就可以通过process 中的RoundEnvironment去获取,扫描到的所有元素,通过env.getElementsAnnotatedWith,我们可以获取被@BindView注解的元素的列表,其中validateElement校验元素是否可用。

这里写图片描述

env.getElementsAnnotatedWith返回的是所有被@BindView注解的一个列表,有时候我们需要加个判断,比如这个元素是否是个类。

@Override  public boolean process(Set<? extends TypeElement> an, RoundEnvironment env) {    for (Element e : env.getElementsAnnotatedWith(BindView.class)) {      // 检查元素是否是一个类      if (ae.getKind() != ElementKind.CLASS) {            ...      }   }

错误处理,在处理器中,我们不能直接抛出一个异常,因为在process()中抛出一个异常,会导致运行注解处理器的JVM崩溃,导致跟踪栈信息十分混乱。因此,注解处理器就有一个Messager类,一般通过messager.printMessage( Diagnostic.Kind.ERROR, StringMessage, element)即可正常输出错误信息。

编译时注解是在编译时生成java文件,将生成的java文件注入到源码中,不像运行时注解那样通过反射机制浪费效率。

原创粉丝点击