【 Previous words 】

  • To understand many open source libraries like Arouter, Dagger,Butter Knife, you have to understand annotations;
  • To improve development efficiency and code quality, annotations can help a lot.

What are annotations

Java.lang. annotation, the interface annotation, was introduced in JDK5.0 and later. Annotations are special tags in your code that can be read at compile time, class load time, run time, and perform processing accordingly. Using annotations, developers can embed additional information in source files without changing the original logic. Code analysis tools, development tools, and deployment tools can use this supplementary information to validate, process, or deploy.

Reading at run time is handled using the Java reflection mechanism.

The Annotation doesn’t run, it just has member variables, it has no methods. Annotations are part of a program element in the same way that modifiers such as public and final are. Annotations cannot be used as a program element.

In fact, most people have used annotations, but they may not have a thorough understanding of their principles and functions, such as @Override, @deprecated, etc.

1. The role of annotations

Annotations make repetitive tasks automatic, simplifying and automating the process. For example, it is used to generate Java Doc, check the format during compilation, and automatically generate code to improve the quality of software and improve the production efficiency of software.

2. What are the annotations

We usually use annotations from JDK included, Android SDK included, can also be customized. 2.1 meta annotations defined by the JDK

Java provides four meta-annotations that are specifically responsible for creating new annotations, that is, annotating other annotations.

  • Target

    Defines the range of objects that the Annotation modifies. The value is:
    • ElementType.CONSTRUCTOR: describes the constructor
    • ElementType.FIELD: Describes a domain
    • ElementType.LOCAL_VARIABLE: Describes local variables
    • ElementType.METHOD: Describes a method
    • ElementType.PACKAGE: Describes packages
    • ElementType.PARAMETER: Describes parameters
    • ElementType.TYPE: Describes classes, interfaces (including annotation types), or enum declarations
  • Retention

    Defines the length of time the Annotation is retained. The value can be:

    RetentionPoicy.SOURCEAnnotations are only retained in the source file. When a Java file is compiled into a class file, annotations are discarded. It is used to perform some inspection operations, such as@Override@SuppressWarnings

    RetentionPoicy.CLASS:Annotations are kept in the class file, but are discarded when the JVM loads the class file, which is the default lifecycle; Used for pre-processing operations at compile time, such as generating auxiliary code (e.gButterKnife)

    RetentionPoicy.RUNTIMEAnnotations are not only saved to the class file, but still exist after the CLASS file is loaded by the JVM. Used to dynamically retrieve annotation information at run time.

  • Documented, used to describe other types of annotations that should be treated as a public API for the annotated program members and therefore can be Documented without assigning by tools such as Javadoc.

  • Inherited, which allows a subclass to inherit annotations from its parent. This is a bit confusing at first, but I need to break it down and allow subclasses to inherit comments from their superclass. Example:

@Target(value = ElementType.TYPE)  

@Retention(RetentionPolicy.RUNTIME)

@Inherited

public @interface Sample {

public String name() default "";

}





@Sample

class Test{

}



class Test2 extents Test{

}

Copy the code

This class Test2 actually has an annotation @sample.

If the member name is value, it can be abbreviated during assignment. If the member type is array, but only one element is assigned, it can also be abbreviated. The following three forms are equivalent.

Normal writing

@Documented

@Retention(value = RetentionPolicy.RUNTIME)

@Target(value = {ElementType.ANNOTATION_TYPE})

public @interface Target {

ElementType[] value();

}

Copy the code

Omitting value (only if the member name is value)

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.ANNOTATION_TYPE})

public @interface Target {

ElementType[] value();

}

Copy the code

The member type is an array, which assigns only one element

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.ANNOTATION_TYPE)

public @interface Target {

ElementType[] value();

}

Copy the code

* 2.2 Other built-in annotations in the JDK *

@Override, @Deprecated, @SuppressWarnings, @Safevarargs, @FunctionalInterface, @Resources, etc.

* 2.3 Android SDK built-in annotations *

Android SDK built-in annotations are in the package. Com Android. Support: support – in the annotations, below to ‘com. Android. Support: support – annotations: 25.2.0’, for example

  • Resource reference restriction class: The restriction parameter must be the corresponding resource type

    AnimRes @anyres @arrayres @attrres @BoolRes @colorRes etc
  • Thread execution restriction class: Used to restrict methods or classes that must be executed in a specified thread

    @AnyThread @BinderThread @MainThread @UiThread @WorkerThread
  • Parameter null limit class: used to restrict whether a parameter can be null

    @NonNull @Nullable
  • Type range limiting class: Used to limit the range of values for a token value

    @FloatRang @IntRange
  • Type definition class: a collection of values used to restrict defined annotations

    @IntDef @StringDef
  • Additional functional annotations:

    @CallSuper @CheckResult @ColorInt @Dimension @Keep @Px @RequiresApi @RequiresPermission @RestrictTo @Size @VisibleForTesting

2. Custom annotations

The most rewarding use of annotations is to customize them to your own needs. Here are three examples of annotation customizations in turn:

1, RetentionPolicy. SOURCE

General function parameter values are limited, such as view. setVisibility parameters are limited, you can see the view. class source code

In addition to IntDef, we have 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);

}



Copy the code

2, RetentionPolicy. The RUNTIME

Runtime annotations are defined as follows:

// Apply classes, interfaces (including annotation types), or enumerations

Retention(RetentionPolicy.RUNTIME)

Target(ElementType.TYPE)

public @interface ClassInfo {

String value();

}

// Applies to field attributes, including enum constants

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.FIELD)

public @interface FieldInfo {

int[] value();

}

// Method of application

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface MethodInfo {

String name() default "long";

int age() default 27;

}

Copy the code

Define a test class to use these annotations:

/ * *

* Test runtime annotations

* /

@ClassInfo("Test Class")

public class TestRuntimeAnnotation {



@FieldInfo(value = {1, 2})

public String fieldInfo = "FiledInfo";



@MethodInfo(name = "BlueBird")

public static String getMethodInfo() {

return return fieldInfo;

}

}

Copy the code

Use notes:

/ * *

* Test runtime annotations

* /

private void _testRuntimeAnnotation() {

StringBuffer sb = new StringBuffer();

Class<? > cls = TestRuntimeAnnotation.class;

Constructor<? >[] constructors = cls.getConstructors();

// Get the annotation of the specified type

Sb.append ("Class annotation: ").append("\n");

ClassInfo classInfo = cls.getAnnotation(ClassInfo.class);

if (classInfo ! = null) {

sb.append(cls.getSimpleName()).append("\n");

Sb. Append (" comments: "), append (classInfo. Value ()), append (" \ n \ n ");

}



Sb.append ("Field note: ").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 (" annotations: ").append(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 (" note value: ").append("\n");

sb.append("name: ").append(methodInfo.name()).append("\n");

sb.append("age: ").append(methodInfo.age()).append("\n");

}

}



System.out.print(sb.toString());

}

Copy the code

All you do is get the corresponding element by reflection, get the annotation on the element, and finally get the attribute value of the annotation. Run-time annotations are somewhat inefficient because of reflection, and many open source projects now use compile-time annotations.

3, RetentionPolicy. CLASS

* 3.1 Adding dependencies *

If the Gradle plugin is above 2.2, you do not need to add the following Android-apt dependencies.

The classpath 'com. Android. Tools. Build: gradle: 2.2.1'

Copy the code

Add android-apt dependencies to build.gradle for the entire project

buildscript {  

repositories {

jcenter()

mavenCentral() // add

}

dependencies {

Classpath 'com. Android. Tools. Build: gradle: 2.1.2' / / above 2.2 without adding apt rely on

The classpath 'com. Neenbedankt. Gradle. Plugins: android - apt: 1.8' / / add

}

}

Copy the code

Android-apt: Android-apt is a Gradle plugin that assists Android Studio with annotation processors. It has two purposes:

  • Allows configurations to be used only as annotations processor dependencies at compile time and not added to the final APK or library
  • Set the source path so that the code generated by the annotation handler is properly referenced by Android Studio

With the release of Android Gradle plugin version 2.2, the author of Android-apt recently confirmed on the official website that he will not continue to maintain Android-apt, and recommended that you use the same capabilities provided by the official Android plugin. Android-apt, which was released about three years ago, is about to be replaced by The Android Gradle plugin called annotationProcessor.

Annotations Create a Java library for storing annotations (annotations)

@Retention(RetentionPolicy.CLASS)  

@Target(ElementType.TYPE)

public @interface MyAnnotation {

String value();

}

Copy the code

Create a Java library named javax.processors (processors) and plugin. javax.processors (processors)

Build. gradle depends on the following:

The compile 'com. Google. Auto. Services: auto - service: 1.0 rc2'

The compile 'com. Squareup: javapoet: 1.7.0'



compile(project(':annotations'))

Copy the code

Among them, the auto – service is used to automatically in the meta-inf/services directory folder to create javax.mail. Annotation. Processing. The Processor files; Javapoet is a helper library for generating.java source files. It is very convenient to help us generate the.java source files we need

Example:

package com.example;



import com.google.auto.service.AutoService;

import com.squareup.javapoet.JavaFile;

import com.squareup.javapoet.MethodSpec;

import com.squareup.javapoet.TypeSpec;



import java.io.IOException;

import java.util.LinkedHashSet;

import java.util.Set;



import javax.annotation.processing.AbstractProcessor;

import javax.annotation.processing.Filer;

import javax.annotation.processing.ProcessingEnvironment;

import javax.annotation.processing.Processor;

import javax.annotation.processing.RoundEnvironment;

import javax.lang.model.SourceVersion;

import javax.lang.model.element.Element;

import javax.lang.model.element.ElementKind;

import javax.lang.model.element.Modifier;

import javax.lang.model.element.TypeElement;



@AutoService(Processor.class)

public class MyProcessor extends AbstractProcessor {



private Filer filer;



@Override

public synchronized void init(ProcessingEnvironment processingEnv) {

super.init(processingEnv);

// Filer is an interface that supports the creation of new files through annotation handlers

filer = processingEnv.getFiler();

}



@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

for (TypeElement element : annotations) {

// Create a new file

if (element.getQualifiedName().toString().equals(MyAnnotation.class.getCanonicalName())) {

Create the main method

MethodSpec main = MethodSpec.methodBuilder("main")

.addModifiers(Modifier.PUBLIC, Modifier.STATIC)

.returns(void.class)

.addParameter(String[].class, "args")

.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!" )

.build();

Create the HelloWorld class

TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL)

.addMethod(main)

.build();



try {

/ / generated com. Example. The HelloWorld. Java

JavaFile javaFile = JavaFile.builder("com.example", helloWorld)

.addFileComment(" This codes are generated automatically. Do not modify!" )

.build();

// Generate the file

javaFile.writeTo(filer);

} catch (IOException e) {

e.printStackTrace();

}

}

}



// Print logs on Gradle Console

for (Element element : roundEnv.getElementsAnnotatedWith(MyAnnotation.class)) {

System.out.println("------------------------------");

// Determine the type of the element as Class

if (element.getKind() == ElementKind.CLASS) {

// Displays the conversion element type

TypeElement typeElement = (TypeElement) element;

// Prints the element name

System.out.println(typeElement.getSimpleName());

/ / output annotation attribute value System. Out. Println (typeElement. GetAnnotation (MyAnnotation. Class). The value ());

}

System.out.println("------------------------------");

}

return true;

}



@Override

public Set<String> getSupportedAnnotationTypes() {

Set<String> annotataions = new LinkedHashSet<String>();

annotataions.add(MyAnnotation.class.getCanonicalName());

return annotataions;

}



@Override

public SourceVersion getSupportedSourceVersion() {

return SourceVersion.latestSupported();

}

}



Copy the code

3.4 Using defined annotations in code * : Rely on annotations and Processors, the two Java libraries above

import com.example.MyAnnotation;



@MyAnnotation("test")

public class MainActivity extends AppCompatActivity {



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

}

Copy the code

When compiled, the specified file with the specified package name is generated, as shown:

In the initialization interface of the custom annotation processor, the following four auxiliary interfaces can be obtained:

public class MyProcessor extends AbstractProcessor {  



private Types typeUtils;

private Elements elementUtils;

private Filer filer;

private Messager messager;



@Override

public synchronized void init(ProcessingEnvironment processingEnv) {

super.init(processingEnv);

typeUtils = processingEnv.getTypeUtils();

elementUtils = processingEnv.getElementUtils();

filer = processingEnv.getFiler();

messager = processingEnv.getMessager();

}

}

Copy the code

Types: Types is a tool for handling TypeMirror Elements: Elements is a tool for handling Element Filer: Normally we will use it in conjunction with JavaPoet to generate what we need. Messager provides the annotation handler with a way to report errors, warnings, and prompts

* 3.5 Annotated libraries available to third parties *

The following example uses gradle plugin 2.2 by default and no longer uses apt

A typical library that uses compile-time annotations has three modules:

  • Annotations module, Java library, XXXX-Annotations
  • Module to implement annotator, Java library, XXXX-compiler
  • Provide external interface module, Android library, XXXX-API

Module xxxx-API dependencies {annotationProcessor ‘XXXX-compiler :1.0.0′ compile’ xxxx-Annotations :1.0.0′ //…. others }

When used by a third party, it can be relied on as follows:

dependencies {

.

The compile 'com. Google. Dagger: a dagger: 2.9'

AnnotationProcessor 'com. Google. Dagger: a dagger - compiler: 2.9'

}

Copy the code

See AnnotationSample sample project

Reference link: www.jb51.net/article/802… Blog.csdn.net/github_3518… Blog.csdn.net/github_3518… www.zhihu.com/question/36… Github.com/alibaba/ARo… Blog.csdn.net/asce1885/ar…