Android注解(Annotation)知识点总结整理

来源:互联网 发布:网络武侠小说排行榜 编辑:程序博客网 时间:2024/05/19 02:45

Java提供了Annotations来对代码提供一些注解,以解释、约束代码,也可以称为编程的元数据。到底什么是Annotation呢,举几个大家熟悉的例子如@Override(表示重载)、@Deprecated(表示下述方法不推荐使用,通常标注为此会提供推荐方法)等,Butter Knife也是Android开发常用的库,GitHub上Star达到了6200+,它在简化代码时主要用到的也是注解技巧。

@Override public void onCreate(Bundle savedInstanceState);Butter Knife @Bind(R.id.user) EditText username;

之所以叫Android Annotations,是因为Android的注解比Java多提供了一个库“android.support:support-annotations”,可以说是Java注解的扩展。接下来本文经过查证参考,总结整理了其基本的概念,因为翻译水平有限,涉及定义的地方将对文档原文有所引用。望各位批评指正~

  • 定义

先摘录Wiki和Oracle的原文定义如下:

       Wiki

       An annotation, in the Java computer programming language, is a form of syntactic metadata that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Unlike Javadoc tags, Java annotations can be reflective in that they can be embedded in class files generated by the compiler and may be retained by the Java VM to be made retrievable at run-time. It is possible to create meta-annotations out of the existing ones in Java.

      Oracle

      Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.

Annotation,中文一般译为“注解”,表示为程序语言的元数据格式,可以添加如Java的源代码中,类、方法、变量、参数、包都可以被注解。不同于Javadoc的标签,Java的注解如果设置为留存到VM可识别的运行时态,那么它是可以通过反射获取出来的。注解本身对他们所注解的代码没有直接的影响。

  • 类型

很多资料对Java Annotation进行了分类,大部分是分为:Java内置、元注解、自定注解。这里因为考虑到Android库提供的注解,我将其总结为以下几种类型:

Built-in Java Annotations(java.lang)
Meta-annotations(java.lang.annotation)
Android Support Annotations(android.support:support-annotations)
Marker Annotation
User-defined Annotations
具体描述如下:

Built-in Java Annotations(java.lang)

Java内置注解;定义在java.lang中,最常用的三个是@Deprecated、@Overrride、@SuppressWarnings。后续补充了两个@SafeVarargs和@FunctionsalInterface,后者是在Java 8添加的。具体的介绍如下:

@Deprecated
@Deprecated annotation indicates that the marked element is deprecated and should no longer be used.
指示该元素已过时,应该不再被使用。一般在指定时会在注释中补充为什么不推荐使用,需要用哪种方式替代。

@Override
@Override annotation informs the compiler that the element is meant to override an element declared in a superclass.
告诉编译器这个方法是重载了父类的方法。在代码书写时,IDE会对方法、参数等进行检查。

@SuppressWarnings 
@SuppressWarnings annotation tells the compiler to suppress specific warnings that it would otherwise generate.
告诉编译器忽略它会产生的警告。
@SafeVarargs
–Claims to the compiler that the annotation target does nothing potentially unsafe to its varargs argument.
@FunctionalInterface(Java SE 8)
–An informative annotation type used to indicate that an interface type declaration is intended to be a functional interface as defined by the Java Language Specification. 


Meta-annotations(java.lang.annotation)

元注解;定义在java.lang.annotation中,定义为用来描述注解的注解(Annotations that apply to other annotations are called meta-annotations. )。常见的四个类型是@Documented、@Inherited、@Retention、@Target。在Java 8新增了两个:@Repeatable和@Native。具体描述如下:

@Documented

Indicates that annotations with a type are to be documented by javadoc and similar tools by default.
指示该注解是否默认通过Javadoc或者类似的工具进行文档化,是一种语义元注解。

通过这种方式定义,并使用了Foo注解,使用Android Studio的Tools->Generate JavaDoc 生成的结果中,对Foo的解释:

将上述注解定义中@Documented去掉,重新生成JavaDoc,结果是:

@Documented就是控制该注解是否写进JavaDoc文档中。

使用@Documented并不是把Annotation转化成JavaDoc注释语言形如:

而是直接写入文档。


@Inherited

Indicates that an annotation type is automatically inherited.

指示注解类型被自动继承。如果在解析注解时发现了该字段,并且在该类中没有该类型的注解,则对其父类进行查询。举个例子,如果一个类中,没有A注解,但是其父类是有A注解的,并且A注解是被@Inherited注解的(不妨认为保留时态是Runtime),那么使用反射获取子类的A注解时,因为获取不到,所以会去其父类查询到A注解。使用了@Inherited注解的类,这个注解是可以被用于其子类。

@Retention

Indicates how long annotations with the annotated type are to be retained.

指示该注解的保留时间,即表明注解的生存周期。参数分为以下几类:

RetentionPolicy.SOURCE 
The marked annotation is retained only in the source level and is ignored by the compiler.
该注解只保留到代码层,编译器将对其忽略。因此其不会出现在生成的class文件中。

RetentionPolicy.CLASS
The marked annotation is retained by the compiler at compile time, but is ignored by the Java Virtual Machine (JVM).
该注解保留在编译器中,因此能出现在生成的class文件中,但是被JVM忽略,所以不能在运行时获取注解内容。

RetentionPolicy.RUNTIME
The marked annotation is retained by the JVM so it can be used by the runtime environment.
该注解能保留在JVM中,可以在运行时通过反射的方法获取具体内容。

如果自定义注解时不进行指定,默认为RetentionPolicy.CLASS。

@Target

Indicates the contexts in which an annotation type is applicable.
指定注解的目标,给什么类型注解,参数如下:
ElementType.ANNOTATION_TYPE 
–can be applied to an annotation type.给注解注解
ElementType.CONSTRUCTOR 
–can be applied to a constructor.给构造方法注解
ElementType.FIELD 
–can be applied to a field or property. 给字段注解,不要忘了,字段可以是对象
ElementType.LOCAL_VARIABLE 
–can be applied to a local variable. 给局部变量注解
ElementType.METHOD 
–can be applied to a method-level annotation.给方法注解
ElementType.PACKAGE 
–can be applied to a package declaration.给包注解
ElementType.PARAMETER 
–can be applied to the parameters of a method.给参数注解
ElementType.TYPE 
–can be applied to any element of a class.   给类(型)注解

ElementType 常量在 Target 注解中至多只能出现一次,如下是非法的:@Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})

@Repetable

The annotation type java.lang.annotation.Repeatable is used to indicate that the annotation type whose declaration it (meta-)annotates is repeatable.
指示该注解是否可以多次使用。
(例子来自http://www.cnblogs.com/guangshan/p/4886029.html)
在该注解出现之前,如果需要重复使用同样的注解编译器会报错,只能用以下方法通过声明一个注解数组来表示,如下:
public @interface Authority {     String role();} public @interface Authorities {    Authority[] value();} public class RepeatAnnotationUseOldVersion {    @Authorities({@Authority(role="Admin"),@Authority(role="Manager")})    public void doSomeThing(){    }}

 缺点是可读性很差,正违背了注解本身的含义。使用@Repetable后,可以直接对其重复使用,如下:

@Repeatable(Authorities.class)public @interface Authority {     String role();} public @interface Authorities {    Authority[] value();} public class RepeatAnnotationUseNewVersion {    @Authority(role="Admin")    @Authority(role="Manager")    public void doSomeThing(){ }}


@Native

“Indicates that a field defining a constant value may be referenced from native code.”

用来标记Native的属性。

这是一个比较特殊的注解,在Oracle Tutorial文档中,并没有把它描述成一个meta-annotations( https://docs.oracle.com/javase/tutorial/java/annotations/predefined.html),而在Oracle Java 8文档中,将其放在Annotation Types Summary(之前版本元注解都放在这个类型中解释)的表述中(https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/package-summary.html)。


Android Support Annotations(android.support:support-annotations)

Android扩展注解;android.support:support-annotations提供的一些适用于Android的注解类型,目前更新到22版本。

1、Nullness Annotations
指定参数是否可以为空。

2、资源类型注解
指定参数是何种资源类型:@StringRes, @DrawableRes, @ColorRes, @InterpolatorRes。有种特殊的注解@AnyRes表示可以是任何资源类型:
–getResourceName(@AnyRes int resId)
–getResources().getResourceName(R.drawable.icon)
–getResources().getResourceName(42) 
前两者正确,最后一种方式错误。

3、IntDef/StringDef: 类型定义注解
指示是整型/字符串类型,其中整型除了可以作为资源的引用之外,也可以用作“枚举”类型使用。
@IntDef和”typedef”作用非常类似,可以用@IntDef指定一个期望的整型常量值列表以修饰API(参考引用见最后)。

4、线程注解: @UiThread, @WorkerThread, …
指示运行在哪个线程中,分类如下:
@UiThread
@MainThread
@WorkerThread
@BinderThread
其中Ui线程和主线程有本质上的联系,主线程就是一个Ui线程,Ui线程也必定是主线程。所以很多编译工具(Android Studio、Lint)可以对其进行互换。使用的不同之处是一般把生命周期相关的内容用@MainThread注解,视图相关的内容用@UiThread注解。
以AsyncTask为例(参考引用见最后)

因为doInBackgroud方法使用@WorkerThread修饰,而View相关的默认被@UiThread注解,所以在使用myView.setText方法时编译器会提示这是一个冲突。

5、RGB颜色整型
指定真实的RGB或者ARGB的颜色值。

6、值约束: @Size, @IntRange, @FloatRange
指定值的范围。
@IntRange指定int和long的范围,@FloatRange指定float和double的范围。
@Size的使用方法如下:
–集合不能为空: @Size(min=1)
–字符串最大只能有23个字符: @Size(max=23)
–数组只能有2个元素: @Size(2)
–数组的大小必须是2的倍数 (例如图形API中获取位置的x/y坐标数组: @Size(multiple=2)

7、权限注解: @RequiresPermission
标注元素的权限,可以使用anyof和allof进行逻辑上的整合。


8、方法重写: @CallSuper
标注元素(如重载函数)必须调用被重载元素的方法,即必须调用super。

9、返回值: @CheckResult
Denotes that the annotated method returns a result that it typically is an error to ignore. This is usually used for methods that have no side effect, so calling it without actually looking at the result usually means the developer has misunderstood what the method does.
提醒使用者可能误会了该元素的使用方法,因为其返回值才是能真正有效提供结果的。忽视返回值可能不能起到使用的效果。如下例子,使用trim函数对String进行处理,需要使用其返回值来真正获取处理结果,不然是无效的。

10、@VisibleForTesting
–可以把这个注解标注到类、方法或者字段上,以便在测试的时候可以使用。

这个Annotation只是一个指示作用,告诉其他开发者该函数为什么有这么大的可见程度(为了测试单元或者其他类对其测试使用)。

因此经常用来修饰public和protected,用其修饰private不会报错,但是意义很小。

它不能改变任何权限。

Guava has a @VisibleForTesting annotation, but it's only for documentation purpose.

A simple annotation that tells you why a particular property access restriction has been relaxed.A common trick to use in testing is to relax access restrictions to default for a particular property, so that you can use it in a unit test, which resides in the same package (though in different catalog). Whether you thing it's good or bad, remember to give a hint about that to the developer.

该注解用来说明,为什么该变量或者函数私有访问权限被释放成“公有”或者“package可见”。

通常来说,一个函数默认

这样定义,则是在package中可访问,而其他package的类无法访问,然而

单元测试形如

对此是有访问权限的。所以加上  @VisibleForTesting是说明,为什么你定义其他类似函数需要用private,这里这个test123函数需要释放私有访问权限呢?哦,原来是需要对测试单元可见(private函数 测试类是访问不了的)。

类似的,该注解经常和public一起使用,告诉大家为什么这个函数我现在设计成public的,是为了给其他测试的类使用的。总的来说@VisibleForTesting就是一个标记(Marker)。

以上都是搜到的解释,所以我写Demo也进行过大量验证,@ VisibleForTesting并不能改变权限,在单元测试以及其他package中,访问权限加不加该注解没有任何改变。

@VisibleForTesting没有那么强大,它只是一个很基本的注解。

This annotation is better than nothing in such a case because it at least makes it clear to others using the construct that there is a reason for its otherwise surprisingly relaxed visibility.

引自http://marxsoftware.blogspot.com/2011/11/two-generally-useful-guava-annotations.html


11、@Keep
–We've also added @Keep to the support annotations. Note however that that annotation hasn't been hooked up to the Gradle plugin yet (though it's in progress.) When finished this will let you annotate methods and classes that should be retained when minimizing the app.


Marker Annotation

标记式注解;不指定任何元素,也不需要默认值,主要作用是对一些声明定义进行标记。

A marker annotation type is an annotation type with no elements, not even one with a default value. Marker Annotations are used to mark a declaration.


User-defined Annotations

自定义注解;



  • 使用

使用方法很简单,如下:


值得注意的是,何时可以使用注解,对其有人已经进行过整理,引用如下:

创建类的实例时
–new @Interned MyObject();
类型转化时
–myString = (@NonNull String) str;
Implements clause
–class UnmodifiableList<T> implements @Readonly List<@Readonly T> { ... }
抛异常时
–void monitorTemperature() throws @Critical TemperatureException { ... }


  • 解析

解析主要是对注解进行分析提取,获取其具体内容的过程,根据其生存周期不同,可以分为两种解析方式:运行时解析和编译时解析。

运行时解析

常用 API为:
method.getAnnotation(AnnotationName.class);
method.getAnnotations();
method.isAnnotationPresent(AnnotationName.class);

上述自定义注解解析可以在运行时进行。



编译时解析

编译时 Annotation 指 @Retention 为 Class的 Annotation,  自动解析。需要做的
1、自定义类集成自 AbstractProcessor
2、重写其中的 process 函数

关于编译时解析,主要问题在于自己如何实现apt(Annotation Processor Tool)的processor,编译环境如何使用它。

1、自定义Annotation;

2、通过apt提供的接口,自定义Annotation processor类,实现主要的处理方法;

3、注册自定的Processor,以便apt识别;

4、在使用的module内指定使用apt来编译该processor。

Demo具体实现方法如下:

新建三个module:

分别是:api、compiler、app。

Api和compiler定义为普通Java项目即可,尤其是compiler,最好是Java项目,因为Android项目不提供apt接口的支持,需要添加插件。

Api:

Java项目:apply plugin: 'java',在此处实现自定义Annotation

Compiler:

Java项目:apply plugin: 'java',在此处实现对Annotation的解析

此处需要让编译器知道你自定义了这个Processor,所以需要在此实现services的注册:

方法一:resources资源文件夹下新建

META-INF/services/javax.annotation.processing.Processor,并将自定义的processor名称加入。形如:

内容为

方法二:使用我用红色框标出来的部分是google提供的库(@AutoService):

   compile 'com.google.auto.service:auto-service:1.0-rc2'

可以通过注解的方式自动添加services注册。

App:

Android项目:

apply plugin: 'com.android.application'

apply plugin: 'com.neenbedankt.android-apt'

该module是指具体功能的实现部分,在此只需要使用注解即可。

三个模块的gradule分别为:

Api:

Compiler:

解析模块依赖api模块。

App:

这里我们可以看到该模块不仅依赖api模块,还需要告诉编译器编译时需要apt对compiler进行解析(apt procject(‘:compiler’))。

有些人建议另开项目,实现自定义Annotation和apt的Processor,并封装成jar包放在Android项目中,原理是一样的。

Build之后,结果如下:这里在解析自定义apt时我只是让其打印了使用Annotation的类和方法名称。


  • 作用

总结整理如下:

1、标记,用于告诉编译器一些信息
2、编译时动态处理,如动态生成代码
3、运行时动态处理,如得到注解信息
4、参数/返回值检查(类型、范围、权限等)
5、定义程序实现标准(Callsuper等)
6、描述项目框架的配置信息(类似XML的作用)
7、优化代码(https://github.com/JakeWharton/butterknife所做的)

  • 参考

http://tutorials.jenkov.com/java/annotations.html

https://docs.oracle.com/javase/tutorial/java/annotations/index.html

http://www.trinea.cn/android/java-annotation-android-open-source-analysis/

http://www.java2s.com/Tutorials/Java/Java_Object_Oriented_Design/0740__Java_Annotation_Type.htm

https://en.wikipedia.org/wiki/Java_annotation#cite_note-3

http://tools.android.com/tech-docs/support-annotations

http://www.flysnow.org/2015/08/13/android-tech-docs-support-annotations.html

http://my.oschina.net/ososchina/blog/345288

http://www.cnblogs.com/guangshan/p/4886029.html

http://www.2cto.com/kf/201502/376988.html

https://github.com/JakeWharton/butterknife

http://brianattwell.com/android-annotation-processing-pojo-string-generator/ http://hannesdorfmann.com/annotation-processing/annotationprocessing101/ http://www.cnblogs.com/avenwu/p/4173899.html https://docs.oracle.com/javase/7/docs/technotes/guides/apt/GettingStarted.html http://www.javalobby.org/java/forums/t17876.html

http://stackoverflow.com/questions/6913325/annotation-to-make-a-private-method-public-only-for-test-classes http://blog.solidcraft.eu/2010/10/googole-guava-v07-examples.html http://www.programcreek.com/java-api-examples/com.google.common.annotations.VisibleForTesting http://programmers.stackexchange.com/questions/100959/how-do-you-unit-test-private-methods

http://marxsoftware.blogspot.com/2011/11/two-generally-useful-guava-annotations.html


0 0
原创粉丝点击