「Android」Android开发你需要知道的注解(Annotation) · Issue #30 · ShowJoy

您所在的位置:网站首页 曹操观沧海中的碣石山是现在的哪里 「Android」Android开发你需要知道的注解(Annotation) · Issue #30 · ShowJoy

「Android」Android开发你需要知道的注解(Annotation) · Issue #30 · ShowJoy

2022-03-26 20:04| 来源: 网络整理| 查看: 265

本文来自尚妆Android团队路飞 发表于尚妆github博客,欢迎订阅!

一、什么是注解 1、注解的作用 2、注解都有哪些 二、自定义注解 1、RetentionPolicy.SOURCE 2、RetentionPolicy.RUNTIME 3、RetentionPolicy.CLASS

【说在前面的话】

要想看懂很多开源库,如Arouter, dagger,Butter Knife等,不得不先看懂注解; 想更好地提升开发效率和代码质量,注解可以帮上很大的忙;

#一、什么是注解

java.lang.annotation,接口 Annotation,在JDK5.0及以后版本引入。 注解是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用Annotation,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充的信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证、处理或者进行部署。

在运行时读取需要使用Java反射机制进行处理。

Annotation不能运行,它只有成员变量,没有方法。Annotation跟public、final等修饰符的地位一样,都是程序元素的一部分,Annotation不能作为一个程序元素使用。

其实大家都是用过注解的,只是可能没有过深入了解它的原理和作用,比如肯定见过@Override ,@Deprecated等。

##1、注解的作用

注解将一些本来重复性的工作,变成程序自动完成,简化和自动化该过程。比如用于生成Java doc,比如编译时进行格式检查,比如自动生成代码等,用于提升软件的质量和提高软件的生产效率。

##2、注解都有哪些 平时我们使用的注解有来自JDK里包含的,也有Android SDK里包含的,也可以自定义。 2.1、JDK定义的元注解

Java提供了四种元注解,专门负责新注解的创建工作,即注解其他注解。

@target 定义了Annotation所修饰的对象范围,取值: 

ElementType.CONSTRUCTOR:用于描述构造器 ElementType.FIELD:用于描述域 ElementType.LOCAL_VARIABLE:用于描述局部变量 ElementType.METHOD:用于描述方法 ElementType.PACKAGE:用于描述包 ElementType.PARAMETER:用于描述参数 ElementType.TYPE:用于描述类、接口(包括注解类型) 或enum声明

@retention 定义了该Annotation被保留的时间长短,取值:  - RetentionPoicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;用于做一些检查性的操作,比如 @Override 和 @SuppressWarnings  - RetentionPoicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;用于在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife)  - RetentionPoicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;用于在运行时去动态获取注解信息。  

@documented 标记注解,用于描述其它类型的注解应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化,不用赋值。

@inherited 标记注解,允许子类继承父类的注解。 这里一开始有点理解不了,需要断句一下,允许子类继承父类的注解。示例:

Target(value = ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface Sample { public String name() default ""; } @Sample class Test{ } class Test2 extents Test{ }

这样类Test2其实也有注解@sample 。

如果成员名称是value,在赋值过程中可以简写。如果成员类型为数组,但是只赋值一个元素,则也可以简写。 示例以下三个写法都是等价的。

正常写法

@Documented @Retention(value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.ANNOTATION_TYPE}) public @interface Target { ElementType[] value(); }

省略value的写法(只有成员名称是value时才能省略)

@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.ANNOTATION_TYPE}) public @interface Target { ElementType[] value(); }

成员类型是数组,只赋值一个元素的简写

@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }

** 2.2 JDK内置的其他注解 **

@Override、@Deprecated、@SuppressWarnings、@SafeVarargs、@FunctionalInterface、@Resources等。

** 2.3 Android SDK内置的注解 **

Android SDK 内置的注解都在包com.android.support:support-annotations里,下面以'com.android.support:support-annotations:25.2.0'为例

资源引用限制类:用于限制参数必须为对应的资源类型 @AnimRes @AnyRes @ArrayRes @AttrRes @BoolRes @ColorRes等 线程执行限制类:用于限制方法或者类必须在指定的线程执行 @AnyThread @BinderThread @MainThread @UiThread @WorkerThread 参数为空性限制类:用于限制参数是否可以为空 @NonNull @Nullable 类型范围限制类:用于限制标注值的值范围 @FloatRang @IntRange 类型定义类:用于限制定义的注解的取值集合 @IntDef @StringDef 其他的功能性注解: @CallSuper @CheckResult @ColorInt @Dimension @Keep @Px @RequiresApi @RequiresPermission @RestrictTo @Size @VisibleForTesting 二、自定义注解

使用收益最大的,还是需要根据自身需求自定义注解。下面依次介绍三种类型的注解自定义示例:

1、RetentionPolicy.SOURCE

一般函数的参数值有限定的情况,比如View.setVisibility 的参数就有限定,可以看到View.class源码里

除了IntDef,还有StringDef

@IntDef({VISIBLE, INVISIBLE, GONE}) @Retention(RetentionPolicy.SOURCE) public @interface Visibility {} public static final int VISIBLE = 0x00000000; public static final int INVISIBLE = 0x00000004; public static final int GONE = 0x00000008; public void setVisibility(@Visibility int visibility) { setFlags(visibility, VISIBILITY_MASK); } 2、RetentionPolicy.RUNTIME

运行时注解的定义如下:

// 适用类、接口(包括注解类型)或枚举 Retention(RetentionPolicy.RUNTIME) Target(ElementType.TYPE) public @interface ClassInfo { String value(); } // 适用field属性,也包括enum常量 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface FieldInfo { int[] value(); } // 适用方法 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MethodInfo { String name() default "long"; int age() default 27; }

定义一个测试类来使用这些注解:

/** * 测试运行时注解 */ @ClassInfo("Test Class") public class TestRuntimeAnnotation { @FieldInfo(value = {1, 2}) public String fieldInfo = "FiledInfo"; @MethodInfo(name = "BlueBird") public static String getMethodInfo() { return return fieldInfo; } }

使用注解:

cls = TestRuntimeAnnotation.class; Constructor[] constructors = cls.getConstructors(); // 获取指定类型的注解 sb.append("Class注解:").append("\n"); ClassInfo classInfo = cls.getAnnotation(ClassInfo.class); if (classInfo != null) { sb.append(cls.getSimpleName()).append("\n"); sb.append("注解值: ").append(classInfo.value()).append("\n\n"); } sb.append("Field注解:").append("\n"); Field[] fields = cls.getDeclaredFields(); for (Field field : fields) { FieldInfo fieldInfo = field.getAnnotation(FieldInfo.class); if (fieldInfo != null) { sb.append(field.getName()).append("\n"); sb.append("注解值: ").append(Arrays.toString(fieldInfo.value())).append("\n\n"); } } sb.append("Method注解:").append("\n"); Method[] methods = cls.getDeclaredMethods(); for (Method method : methods) { MethodInfo methodInfo = method.getAnnotation(MethodInfo.class); if (methodInfo != null) { sb.append(Modifier.toString(method.getModifiers())).append(" ") .append(method.getName()).append("\n"); sb.append("注解值: ").append("\n"); sb.append("name: ").append(methodInfo.name()).append("\n"); sb.append("age: ").append(methodInfo.age()).append("\n"); } } System.out.print(sb.toString()); }

所做的操作都是通过反射获取对应元素,再获取元素上面的注解,最后得到注解的属性值。因为涉及到反射,所以运行时注解的效率多少会受到影响,现在很多的开源项目使用的是编译时注解。

3、RetentionPolicy.CLASS

** 3.1 添加依赖 **

如果Gradle 插件是2.2以上的话,不需要添加以下android-apt依赖。

classpath 'com.android.tools.build:gradle:2.2.1'

在整个工程的 build.gradle 中添加android-apt的依赖

buildscript { repositories { jcenter() mavenCentral() // add } dependencies { classpath 'com.android.tools.build:gradle:2.1.2' //2.2以上无需添加apt依赖 classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' // add } }

android-apt: android-apt 是一个Gradle插件,协助Android Studio 处理annotation processors, 它有两个目的:

允许配置只在编译时作为注解处理器的依赖,而不添加到最后的APK或library 设置源路径,使注解处理器生成的代码能被Android Studio正确的引用

伴随着 Android Gradle 插件 2.2 版本的发布,近期 android-apt 作者在官网发表声明证实了后续将不会继续维护 android-apt,并推荐大家使用 Android 官方插件提供的相同能力。也就是说,大约三年前推出的 android-apt 即将告别开发者,退出历史舞台,Android Gradle 插件提供了名为 annotationProcessor 的功能来完全代替 android-apt。

** 3.2 定义要使用的注解 ** 建一个Java库来专门放注解,库名为:annotations 定义注解

@Retention(RetentionPolicy.CLASS) @Target(ElementType.TYPE) public @interface MyAnnotation { String value(); }

** 3.3 定义注解处理器 ** 另外建一个Java库工程,库名为:processors 这里必须为Java库,不然会找不到javax包下的相关资源

build.gradle 要依赖以下:

compile 'com.google.auto.service:auto-service:1.0-rc2' compile 'com.squareup:javapoet:1.7.0' compile(project(':annotations'))

其中, auto-service 自动用于在 META-INF/services 目录文件夹下创建 javax.annotation.processing.Processor 文件; javapoet用于产生 .java 源文件的辅助库,它可以很方便地帮助我们生成需要的.java 源文件

示例:



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3