First of all, what is an annotation? @Override is an annotation, and its function is:
1. It is reflected in: checking whether the method name and parameter type overridden by the subclass are correct; checking whether the method private/final/static cannot be overridden. In fact, @Override has no actual impact on the application, which can be seen from its source code. 2. Mainly shows the readability of the code. As a well-known annotation in Android development, Override is just one manifestation of annotation. More often, annotations have the following functions:
1. Quick reading of annotation basics 1. Meta-annotation Meta-annotation is a basic annotation provided by Java, which is responsible for annotating other annotations. As shown in the figure above, Override is modified by @Target and @Retention, which are used to explain other annotations and are located in the sdk/sources/android-25/java/lang/annotation path. The meta annotations are:
@Retention The Retention statement indicates the life cycle of the annotation, and the corresponding RetentionPolicy enumeration indicates when the annotation takes effect:
As shown in Figure X1 below, the Nullable annotation in com.android.support:support-annotations will determine at compile time whether the annotated parameter will be empty, which will be analyzed later. @Target Target indicates the applicable scope of the annotation, corresponding to the ElementType enumeration, which clarifies the effective scope of the annotation.
As shown in Figure X1 above, @Nullable can be used to annotate methods, parameters, class members, annotations, and package declarations. Common examples are as follows:
@Inherited The class to which the annotation applies cannot inherit the annotation of the parent class by default when it is inherited, unless the annotation declares @Inherited. At the same time, the annotation declared by Inherited is only valid for the class, not for the method/attribute. As shown in the code below, the annotation class @AInherited declares Inherited, while the annotation BNotInherited does not, so they are modified:
2. Custom annotations 2.1 Runtime Annotations After understanding meta-annotations, let's see how to implement and use custom annotations. Here we briefly introduce the runtime annotation RUNTIME, and the compile-time annotation CLASS will be analyzed later. First, create an annotation conforming to: public @interface annotation name {method parameter}, such as the following @getViewTo annotation:
Then, as shown below, we describe the annotations in the Activity's member variables mTv and mBtn. When the App is running, the controls obtained by findViewbyId are injected into mTv and mBtn through reflection. Does it look familiar? It has a bit of ButterKnife flavor? Of course, ButterKnife is much more advanced than this. After all, more reflection affects efficiency. However, we understand that we can inject and create objects through annotations, which can save code to a certain extent.
2.2 Compile-time annotations Runtime annotation RUNTIME As shown in 2.1 above, most of the time, reflection is used at runtime to achieve the desired effect, which greatly affects efficiency. If each View injection of BufferKnife is impossible, how can it be achieved? In fact, ButterKnife uses the compile-time annotation CLASS, as shown in Figure X2.2 below, which is ButterKnife's @BindView annotation. It is a compile-time annotation that generates corresponding Java code at compile time to achieve injection. When it comes to compile-time annotations, we have to talk about the annotation processor AbstractProcessor. If you have noticed, third-party annotation-related class libraries, such as bufferKnike and ARouter, generally have a Module named Compiler, as shown in Figure X2.3. These are generally annotation processors used to process corresponding annotations at compile time. Annotation Processor is a tool of javac, which is used to scan and process annotations at compile time. You can customize annotations and register corresponding annotation processors to process your annotation logic. As shown below, implement a custom annotation processor, rewrite at least four methods, and register your custom Processor. For details, refer to the CustomProcessor code below.
First, let's sort out the general processor processing logic:
Then, let's understand a concept: Element, because it is the basis for us to obtain annotations. During the Processor process, all Java source codes are scanned. Each part of the code is a specific type of Element, which is like a hierarchical structure of XML, such as classes, variables, methods, etc. Each Element represents a static, language-level component, as shown in the code below.
Element represents the source code, while TypeElement represents the type element in the source code, such as a class. However, TypeElement does not contain information about the class itself. You can get the name of the class from TypeElement, but you cannot get information about the class, such as its parent class. This information needs to be obtained through TypeMirror. You can get the TypeMirror of an element by calling elements.asType(). 1. Knowing Element, we can get all the scanned elements through RoundEnvironment in process, as shown in Figure X2.4. Through env.getElementsAnnotatedWith, we can get the list of elements annotated by @BindView, where validateElement verifies whether the element is available. 2. Because env.getElementsAnnotatedWith returns a list of all elements annotated with @BindView, sometimes we need to make some additional judgments, such as checking whether these elements are a class:
3. javapoet (com.squareup:javapoet) is an open source library that generates java files according to specified parameters. If you are interested in learning about javapoet, you can take a look at javapoet - it frees you from repetitive and boring code. In the processor, after creating a JavaFile according to the parameters, you can use javaFile.writeTo(filer) through Filer to generate the java file you need. 4. Error handling: In the processor, we cannot directly throw an exception, because throwing an exception in process() will cause the JVM running the annotation processor to crash, resulting in very confusing stack trace information. Therefore, the annotation processor has a Messager class, which can normally output error information through messagger.printMessage( Diagnostic.Kind.ERROR, StringMessage, element). At this point, your annotation processor has completed all the logic. It can be seen that compile-time annotations actually generate Java files during compilation, and then inject the generated Java files into the source code. At runtime, they do not affect efficiency and resources like runtime annotations. Summarize Let's use ButterKnife's process and give a simple example to summarize.
OK, through the above process, have you connected the generation and use of compile-time annotations? If you have any questions, please leave a message to discuss. |
<<: Apple acquires French image recognition company, may embed technology into iPhone
>>: How to maximize HTML5 performance
There are so many seasonal fruits in autumn, but ...
"Why does my ID number have a cross at the e...
Mixed Knowledge Specially designed to cure confus...
On October 4, 1957, the world's first artific...
Many people understand that I often change my ide...
When the weather gets hot, the first thing people...
On May 7, the Shanghai Municipal Epidemic Preventi...
In the field of Internet TV, Whale can be said to...
Product strategy is not based on features, but on...
In July this year, Daimler released the latest Me...
Audit expert: Wang Guoyi Postdoctoral fellow in N...
In January 2025, there were 181,000 more public c...
As the saying goes, a good horse deserves a good ...
There are more than 1.5 million ATMs in the world...
Although the smart TV industry has continued to b...