美文网首页
Android中应该了解的注解知识(Android进阶之光笔记)

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

作者: YangDxg | 来源:发表于2019-03-12 16:58 被阅读0次

    注解

    本文讲解一些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也不需要做修改

    相关文章

      网友评论

          本文标题:Android中应该了解的注解知识(Android进阶之光笔记)

          本文链接:https://www.haomeiwen.com/subject/ixvnpqtx.html