Android中应该了解的注解知识(Android进阶之光笔记)

注解

本文讲解一些Android中用到的基本注解只是及ButterKnife和Dagger2原理

注解分类

注解分为标准注解和元注解

标准注解

  • @Override:对覆盖超类中的方法进行标记,如果被标记的方法并没有实际覆盖超类中的方法,则编译器会发出警告.
  • @Deprecated:对不鼓励使用或者已经过时的方法进行注解,当编程人员使用这些方法时,将会在编译时显示提示信息
  • @SupressWarnings:选择性的取消特定代码中的警告
  • @SafeVarargs:JDK7新增,用来声明使用了可变长度参数的方法,其在与泛型类一起使用时不会出现类型安全问题

元注解

元注解是用来注解其他的注解,从而创建新的注解

  • @Target:注解所修饰的对象范围
  • @Inherited:表示注解可以被继承
  • @Documented:表示这个注解应该被JavaDoc工具记录
  • @Retention:用来声明注解的保留策略
  • @Repeatable:JDK8新增,允许同一注解在同一声明类型(类,属性或方法)上多次使用
@Target注解取值是一个ElementType类型的数组,有以下几种取值
  • ElementType.TYPE:能修饰类,接口和枚举类型
  • ElementType.FIELD:能修饰成员变量
  • ElementType.METHOD:能修饰方法
  • ElementType.PARAMETER:能修饰参数
  • ElementType.CONSTRUCTOR:能够修饰构造方法
  • ElementType.LOCAL_VARIABLE:能修饰局部变量
  • ElementType.ANNOTATION_TYPE:能修饰注解
  • ElementType.PACKAGE:能修饰包
  • ElementType.TYPE_PARAMETER:类型参数声明
  • ElementType.TYPE_USE:使用类型
@Retention注解有三种类型,表示不同级别的保留策略
  • RetentionPolicy.SOURCE:源码级注解,注解信息只会保留在.java源码中,源码在编译后,注解信息被丢弃,不会保留在.class中
  • RetentionPolicy.CLASS:编译时注解,注解信息之后保留在.java源码以及.class中,当运行Java程序时,JVM会丢弃注解信息,不回保留在JVM中
  • RetentionPolicy.RUNTIME:运行时注解,当运行Java程序时,JVM也会保留该注解信息,可以通过反射获取该注解信息

定义注解

基本定义

定义新的注解类型使用@interface关键字,和定义接口很像

    public @interface Swordsman{

    }

使用注解

    @Swordsman
    public class AnnotationTest{

    }

定义成员变量

注解只有成员变量,没有方法,注解的成员变量在注解定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型

    public @interface Swordsman {
        String name();

        int age();
    }

使用该注解时就应该为该注解的成员变量指定值

    @Swordsman(name = "张三",age = 23)
    public class AnnotationTest {

    }

定义成员变量时,使用default关键字为其指定默认值(使用默认值时就不需要传入参数了)

    public @interface Swordsman {
        String name() default "张三丰";

        int age() default 99;
    }

定义运行时注解

用@Retention来设定注解的保留策略,三种策略的生命周期长度为SOURCE《CLASS《RUNTIME,生命周期短的能起作用的地方,生命周期长的也一定能起作用.

  • 如果要在运行时去动态获取注解信息,只能用RetentionPolicy.RUNTIME;
  • 如果要在编译时进行一些预处理,比如生成一些辅助代码,就使用RetentionPolicy.CLASS
  • 如果只要做一些检查性的操作,如@Override和@SuppressWarnings,则可选用RetentionPolicy.SOURCE
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Swordsman {
        String name() default "张三丰";

        int age() default 99;
    }

注解器处理

如果没有处理注解的工具,注解也不会有太大的作用,对于不同的注解有不同的注解处理器,注解器的处理标准

  • 针对运行时注解采用反射机制处理
  • 针对编译时注解采用AbstractProcessor处理
编写运行时注解处理器

运行时注解需要用到反射机制

    @Documented
    @Target(ElementType.METHOD)//定义方法
    @Retention(RetentionPolicy.RUNTIME)
    public @interface GET{
        String value() default "";
    }

上面代码是Retrofit中定义的@GET注解

    @GET(value = "http://baidu.com")
    public String getIpMsg() {
        return "";
    }

写一个简单的注解处理器

    public static void main(String [] args){
        Method[] methods = MainActivity.AnnotationTest.class.getDeclaredMethods();
        for (Method method : methods) {
            MainActivity.AnnotationTest.GET get = method.getAnnotation(MainActivity.AnnotationTest.GET.class);
            System.out.println(get.value());
        }
    }

getDeclaredMethods和getAnnotation俩个反射方法都属于AnnotatedElement接口,Class,Method和Filed等类都实现了该接口,调用getAnnotation方法返回指定类型的注解对象,也就是GET,调用GET的value方法返回从GET对象中提取的元素的值

编译时注解处理器
定义注解

创建Java Library来专门存放注解,Library名为annotations

这个注解类似于ButterKnife的@BindView

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

创建Java Library存放注解处理器,Library命名为processor,配置processor的build.gradle依赖annotations

apply plugin: 'java-library'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    compile project(':annotations')
}

sourceCompatibility = "1.7"
targetCompatibility = "1.7"

编写注解处理器ClassProcessor

//Java7以后,使用下面俩个注解代替对应的方法,但考虑兼容问题,一般还是实现方法
//@SupportedSourceVersion(SourceVersion.RELEASE_8)
//@SupportedAnnotationTypes("com.yangdxg.annotation.cls.BindView")
public class ClassProcessor extends AbstractProcessor {

    /**
     * 被注解处理工具调用,输入processingEnvironment参数
     * processingEnvironment提供很多有用的工具类,如Elements,Types,Filer和Messager等
     * @param processingEnvironment
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
    }

    /**
     * 相当于每个处理器的祝函数main(),这里写扫描,评估和处理注解的代码以及生成java文件,
     * 出入参数roundEnviroment,可以查询出包含特定注解的被注解元素
     * @param set
     * @param roundEnvironment
     * @return
     */
    @Override
    public boolean process(Set set, RoundEnvironment roundEnvironment) {
        return false;
    }

    /**
     * 必须指定的方法,指定这个注解处理器是注册给那个注解的,返回一个字符串的集合,包含本处理器想要处理的注解类型的合法全称
     * @return
     */
    @Override
    public Set getSupportedAnnotationTypes() {
        LinkedHashSet annotations = new LinkedHashSet<>();
        annotations.add(BindView.class.getCanonicalName());
        return annotations;
    }

    /**
     * 指定使用的Java版本
     * 一般返回 SourceVersion.latestSupported()
     *
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}

实现process方法

    @Override
    public boolean process(Set set, RoundEnvironment roundEnvironment) {
        Messager messager = processingEnv.getMessager();
        for (Element element : roundEnvironment.getElementsAnnotatedWith(BindView.class)) {
            if (element.getKind() == ElementKind.FIELD) {
                //使用messager的printMessage方法来打印出注解修饰的成员变量的名称
                messager.printMessage(Diagnostic.Kind.NOTE, "printMessage:" + element.toString());
            }
        }
        return true;
    }
注册注解处理器

为了使用注解处理器,需要用一个服务文件来注册,创建这个服务文件

  • 在processor库的main目录下新建resources资源文件夹
  • 在resources中建立META-INF/services目录文件夹
  • 在META-INF/services中创建javax.annotation.processing.Processor文件,这个文件的内容是注解处理器的名称,这里文件内容是com.yangdxg.processor.ClassProcessor
可以使用AutoService帮助完成上面步骤
  • 添加依赖auto-sercvice
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    compile project(':annotations')
    compile 'com.google.auto.service.auto-service:1.0-rc2'
}
  • 在注解处理器中添加@AutoService(Processor.class)
@AutoService(Processor.class)
public class ClassProcessor extends AbstractProcessor {
  • 在app中添加对注解器的依赖
    compile project(':annotations')
    compile project(':processor')
  • 在Activity中使用注解
    @BindView(value = R.id.tv_text)
    TextView mTextView;
  • 先对工程clean再Make Project,在Gradle Console中就打印出了注解方法
注: printMessage:mTextView
使用android-apt插件

应用了processor库,但注解处理器只在编译处理期间需要用到,编译处理完后就没有实际作用了,而主工程添加了这个库会引入很多不必要的文件,为了解决这个问题引入插件android-apt,它的作用是

  • 仅仅在编译时期去依赖注解处理器所在的函数库并进行工作,但不会打包到APK中
  • 为注解处理器生成的代码设置好路径,以便Android Studio能够找到它
  • 在app的build.gradle中以apt的方式引入注解处理器processor
dependencies {
    annotationProcessor ':processor'

依赖注入的原理

控制反转

为了解决对象之间耦合度过高的问题,提出了IoC理论,用来实现对象之间的解耦,即控制反转,借助第三方实现具有依赖关系的对象之间的解耦

依赖注入

控制反转是获得依赖对象的过程被反转了,控制反转之后,获得依赖对象的过程由自身管理变为由IoC容器主动注入

依赖注入的实现方式

构造方法注入
public class Car{
    private Engine mEngine;
    public Car(Engine engine){
        this.mEngine=engine;
    }
}
Setter方法注入
public class Car{
    private Engine mEngine;
    public void set(Engine engine){
        this.mEngine=engine;
    }
}
接口注入
public interface ICar{
    public void setEngine(Engine engine);
}

Car类实现ICar

public class Car implement ICar{
    private Engine mEngine;
    public void setEngine(Engine engine){
        this.mEngine=engine;
    }
}

通过以上三种方式,Car和Engine解耦合了,Car不关心Engine的实现,即使Engine的类型变换了,Car也不需要做修改

你可能感兴趣的:(Android中应该了解的注解知识(Android进阶之光笔记))