Write in front:
This article introduces some of the basics of annotations, which I’m sure many of you already know, so read on. By the end of this chapter you will have a clear idea of what constitutes a note and how to write a desired note. This chapter introduces the familiar approach to runtime annotations that Retrofit uses. At the end of this article is the next chapter: the most comprehensive parsing of CLASS annotations ever, taking you through the implementation of your own BufferKnufe.
Definition of annotations
Annotations, also called metadata. A code level specification. It is a feature introduced in JDK1.5 and later and is on the same level as classes, interfaces, and enumerations. It can be declared before packages, classes, fields, methods, local variables, method parameters, and so on, and used to describe these elements. Annotations do not change the way the compiler compiles or the order in which virtual machine instructions are executed. They are more like special annotations that do nothing by themselves. Either the utility method or the compiler itself is required to read the contents of the annotation and control some action. They can be broadly divided into three categories:
Document: Generate documentation from metadata identified in code.
Code analysis: Code analysis by identifying metadata in the code.
Compile checking: Enables the compiler to perform basic compile checking by identifying metadata in the code.
The second purpose
Because annotations run in a separate JVM, we can use any dependencies that the JVM provides us. In addition, CLASS and SOURCE annotations are processed during recompilation, so they can help us do some complicated preparation work during code compilation. In the case of BufferKnife, during annotation processing, classes such as ***_ViewBinding are generated for our annotation object to process the View.
3. Knowledge Preparation
There are three annotations in the Java JDK: @Override (validation format), @deprecated (marking outdated methods or classes), and @Suppresswarnnings (annotations used to suppress compiler warnings). The details of how each annotation is used are not covered here. We can see a professional explanation by clicking here! Take a look at the @Override source code.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
Copy the code
Through reading the source code, we can see that the way of annotation is @interface. Each annotation needs more than one meta-annotation modification. The meta-annotation here is actually the annotation modification of the annotation, can be understood as the smallest annotation unit. Take a look at the meaning of each comment in detail below:
- @Target :
Specifies the range of objects that the annotation modifies, that is, the object on which we apply the annotation: the annotation can be usedpackages
,types
(Class, interface, enumeration, annotation type), type members (methods, constructors, member variables, enumerated values), method parameters, and local variables (such as loop variables, catch parameters). Used in the annotation type declarationtarget
The goal of its modification can be more clearly defined. The following properties are multi-select states, and we can define multiple annotation scopes, such as: (1).constructor: used to describe the CONSTRUCTOR.
(2).field: used to describe fields, such as class attributes.
(3).LOCAL_VARIABLE: Used to describe local variables.
(4).METHOD: Used to describe methods.
(5).package: Used to describe packages.
(6).PARAMETER: Used to describe parameters.
(7).type: Used to describe classes, interfaces (including annotation types), or enum declarations.
@ Target ({ElementType METHOD, ElementType. FIELD}), the use of a single @ Target (ElementType. FIELD).Copy the code
-
@Retention: Defines how long annotations are retained: some annotations only appear in source code and are discarded by the compiler; Others are compiled in class files; Annotations compiled in the class file may be ignored by the virtual machine, while others will be read when the class is loaded (note that class execution is not affected, since annotations and the class are used separately). Use this meta-annotation to limit the “life cycle” of an Annotation. This means that the annotation processor can handle all three types of annotations, and we can only handle RUNTIME annotations with reflection. From Java. Lang. The annotation. RetentionPolicy enumeration values: (1). SOURCE: effectively in a SOURCE file (i.e., the SOURCE file to retain) compiled into a class file will abandon the annotations. (2).class: this annotation will be discarded when compiled into a dex file if it is valid in a CLASS file. (3).runtime: valid at RUNTIME (that is, reserved at RUNTIME) visible at RUNTIME.
-
Documented: Indicates that other types of annotations should be considered a public API for the annotated program members and therefore Documented by tools such as Javadoc. Documented is a marked annotation that has no member.
-
A meta-annotation is a markup annotation. @Inherited explains that an annotated type is Inherited. When an annotation type with the @Inherited annotation is applied to a class, the annotation will be applied to a child class. Note: The @Inherited annotation types are Inherited by children of the annotated class. A class does not inherit annotations from the interface it implements, and a method does not inherit annotations from the method it overloads. When annotations annotated by @Inherited have a Retention value of retentionPolicy. RUNTIME, the reflection API enhances this inheritance. When we use java.lang.reflect to query an annotation of @Inherited type, reflection code checks work: inspect the class and its parent until the specified annotation type is found or reached the top level of the class inheritance structure.
-
Repeatable @ : Repeatable, Java1.8 new feature, in fact, is to put the annotated annotation into the annotation container of the meta annotation. Repeatability means repeatability or use demo to explain:
@target (ElementType.FIELD) @Retention(retentionPolicy.runtime) public @interface MyCar { MyTag[] value(); ----MyTag here is the container for MyTag annotations. } / / another annotation is an annotation on the returned comments @ Target ({ElementType METHOD, ElementType. FIELD}) @ Retention (RetentionPolicy. CLASS) @repeatable (mycar.class) -------- after adding this property here, our annotation can be repeatedly added to our defined container. Notice the value inside when we define the container annotation class object. public @interface MyTag {........ MyTag String name () default""; int size () default 0 ; } // use @mytag (name ="BWM", size = 100) @MyTag() public Car car; // If our comment does not have @repeatable, this is an errorCopy the code
Repeatable(MyCar. Class), meaning that the current annotation (MyTag) is placed in our value (MyCar. Class) annotation container. So what we get when we reprocess the annotation is our last annotation container (MyCar annotation), which is a little bit of a blunt statement. Here’s the demo:
Public class HomeActivity extends AppCompatActivity {@myTag (name ="BWM", size = 100) @MyTag(name = "The mass",size = 200) ...... I'm using repetition here. Car car; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.activity_home); AnnotationProccessor.instance().inject(this); // Log. E ()"WANG"."HomeActivity.onCreate."+ car.toString()); }} Class<? > aClass = o.getClass(); Field[] declaredFields = aClass.getDeclaredFields();for (Field field:declaredFields) { if(field.getName().equals("car")){ Annotation[] annotations = field.getAnnotations(); for(int i = 0; i <annotations.length; i++) { Annotation annotation = annotations[i]; MyCar = MyCar; MyCar = MyCar; MyCar = MyCar // This is the power of @REPEATable. Class<? extends Annotation> aClass1 = annotation.annotationType(); Log.e("WANG"."AnnotationProccessor.MyCar"+aClass1 ); } MyCar annotation = field.getAnnotation(MyCar.class); MyTag[] value = annotation.value(); for (int i = 0; i <value.length; i++) { MyTag myTag = value[i]; Log.e("WANG"."AnnotationProccessor.MyTag name value is "+myTag.name() ); }} The result is: AnnotationProccessor.MyCarinterface cn.example.wang.routerdemo.annotation.MyCar.1 AnnotationProccessor.MyTag name value Is BMW. 2 AnnotationProccessor. MyTag name value is public. 3Copy the code
Custom runtime annotations
@interface is the key word used to declare annotations, and each annotation needs to indicate its life cycle and scope. You can define values for annotations. That is, annotate the internal definition of the method we need. So annotations can do things for us in their own life cycle. Let’s create a custom annotation that initializers an object property, similar to the Dagger.
public @interface MyTag {
}
Copy the code
The definitions in the notes are also prescriptive:
-
Annotation methods cannot take parameters.
-
Annotation methods return values of type limited to: primitive type, String, Enums, Annotation, or an array of these types.
-
Annotation methods can have default values.
-
Annotations themselves can contain meta-annotations that are used to annotate other annotations.
Let’s give it a try!
Public @interface MyTag {// Declare the return value type, there are no curly braces, you can set the default return value, and then directly";"Ah. String name () default""; Int size () default 0; }Copy the code
So once we’ve defined the annotations we’re going to specify where we’re going to use our custom annotations, right? When do you need it? Since we use reflection to handle annotations, reflection is the process by which the class object is inverted to fetch the contents of the class at runtime. If you are not familiar with reflection, please refer to the basics of reflection. So we define the annotation lifecycle at runtime. And the purpose of the annotation is to assign values to custom attributes, so our scope is FIELD. This defines the basic properties of the bean we want to initialize, with default values. This allows us to use the annotation to create the bean object we need.
@Target(ElementType.FIELD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTag {
String name () default "" ;
int size () default 0 ;
}
Copy the code
Now let’s see how to use our custom annotations!
public class HomeActivity extends AppCompatActivity {
@MyTag(name = "BMW",size = 100)
Car car;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home); AnnotationCar.instance().inject(this); // When the program runs, it will output the Car property value of the class. Log.e("WANG"."Car is "+car.toString()); }}Copy the code
Annotations Without an annotation handler, the annotation is meaningless. In this case, we define a Car property in the Activity, then define our annotations on the Car variable, and assign values to our annotations. We then initialize our annotation handler in the onCreate method. Then run the code, the log will print the Car information, first look at the result!
cn.example.wang.routerdemo E/WANG: Car is Car [name=BMW, size=100]
Copy the code
Here is the code for the “annotation processor”, which we wrote ourselves to handle annotations. In fact, the system comes with the annotation processor, but it is generally used to handle source comments and compile-time comments.
Public class AnnotationCar {private static AnnotationCar AnnotationCar; public static AnnotationCarinstance(){
synchronized (AnnotationCar.class){
if(annotationCar == null){
annotationCar = new AnnotationCar();
}
returnannotationCar; } } public void inject(Object o){ Class<? > aClass = o.getClass(); Field[] declaredFields = aClass.getDeclaredFields();for (Field field:declaredFields) {
if(field.getName().equals("car") && field.isAnnotationPresent(MyTag.class)) { MyTag annotation = field.getAnnotation(MyTag.class); Class<? >type = field.getType();
if(Car.class.equals(type)) {
try {
field.setAccessible(true);
field.set(o, new Car(annotation.name(), annotation.size()));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}
Copy the code
This explains why annotations and reflections enter our knowledge at the same time. Here we first get all the attributes in the class, and then go to find the attribute modified by our annotation MyTag, and then take the value in our annotation, and then assign it to the attribute in our class! So we initialize a property value with an annotation. It’s that simple!
Four summarizes
Runtime annotations are easier to understand, and once you know the basics of reflection and annotations, you can write a little demo. But run-time annotations are the ones we use least often, because reflecting run-time operations is time consuming and we don’t have to compromise app performance because of some code brevity. So runtime annotations are just a way for people to get to know annotations. Next, I will introduce the common way to write annotations.
The next chapter: will be detailed, CLASS annotation comprehensive parsing, package see package will complete their own BufferKnife!
Welcome everyone comment area message points out article error ~ thank everybody see officer! Welcome to continue to pay attention to!
My nuggets my CSDN my simple book Github Demo address, welcome start