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<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        return false;
    }

    /**
     * 必须指定的方法,指定这个注解处理器是注册给那个注解的,返回一个字符串的集合,包含本处理器想要处理的注解类型的合法全称
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        LinkedHashSet<String> 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<? extends TypeElement> 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也不需要做修改

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,585评论 4 365
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,923评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,314评论 0 248
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,346评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,718评论 3 291
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,828评论 1 223
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,020评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,758评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,486评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,722评论 2 251
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,196评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,546评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,211评论 3 240
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,132评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,916评论 0 200
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,904评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,758评论 2 274

推荐阅读更多精彩内容