Android注解学习笔记

本文学习自《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

// TypeElement
public 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文件注入到源码中,不像运行时注解那样通过反射机制浪费效率。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值