当前位置:网站首页>Basic chapter: take you through notes
Basic chapter: take you through notes
2022-07-07 09:49:00 【hudawei996】
It says Android Development : From modularization to componentization ( One ), Many friends came to ask why there was no Demo ah ? The reason why I didn't put it immediately demo The reason is that there are many technical points left unfinished .
Today, let's review the flavor carefully Java among Annotation, That is what we often call annotation .
This paper is carried out in the following order : Metadata -> Yuan notes -> Runtime annotations -> Compile time annotation processor .
- What is metadata (metadata)
Metadata from metadata Translated , The so-called metadata is “ Data about data ”, More commonly, it's data that describes data , Descriptive information about data and information resources . Like a text file , There's a creation time , founder , File size and other data , This can be understood as metadata .
stay java in , Metadata exists in the form of tags java In the code , Its existence does not affect the compilation and execution of program code , Usually, it is used to generate other files or the runtime knows the description information of the code to be run .java In the middle of javadoc and annotation All belong to metadata .
1. What is annotation (Annotation)?
The annotation is from java 5.0 Start to join , Can be used to label packages , class , Method , Variable etc. . Like the ones that we see all the time @Override, Or Android In the source @hide,@systemApi,@privateApi etc.
about @Override, Most people know it, but they don't know why , Today I'm going to talk about Annotation The secret behind , To the body .
- 1.1. Yuan notes
Meta annotations are definitions Notes to notes , yes java Provides us with the basic annotations for defining annotations . stay java.lang.annotation In the package, we can see that there are several meta annotations at present :
@Retention
@Target
@Inherited
@Documented
@interface
Next we'll gather @Override Notes to explain 5 The use of basic annotations .
- 1.1.1. @interface
@interface yes java Keywords used to declare annotation classes in . Use this annotation to indicate that it will automatically inherit java.lang.annotation.Annotation class , The process is left to the compiler .
So if we want to define an annotation, we just need to do the following , With @Override Note for example
public @interface Override {
}
We need to pay attention to : When defining annotations , Cannot inherit other annotations or interfaces .
- 1.1.2. @Retention
@Retention: This annotation is used to define the annotation retention policy , When does the defined annotation class exist ( Source phase or After compiling or Operation phase ). The annotation accepts the following parameters :RetentionPolicy.SOURCE,RetentionPolicy.CLASS,RetentionPolicy.RUNTIME, Its specific use and meaning are as follows :
Annotation retention policy | meaning |
@Retention(RetentionPolicy.SOURCE) | Annotations are reserved only in the source code ,class There is no... In the file |
@Retention(RetentionPolicy.CLASS) | Annotations in source and class It's all in the file , But the runtime does not exist , That is, the runtime cannot get , This policy is also the default retention policy |
@Retention(RetentionPolicy.RUNTIME) | Annotation in source code ,class It exists in the file and can be obtained by reflection mechanism at runtime |
Take a look at @Override Retention policy for annotations :
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
This shows that @Override Annotations exist only in the source phase ,javac Remove the annotation during compilation .
- 1.1.3. @Target
This annotation is used to define the purpose of the annotation , Where can annotations be used , For example, it is used for methods or fields , The annotation accepts the following parameters :
Target of action | meaning |
@Target(ElementType.TYPE) | For interfaces ( Annotations are essentially interfaces ), class , enumeration |
@Target(ElementType.FIELD) | For fields , Enumeration constants |
@Target(ElementType.METHOD) | For method |
@Target(ElementType.PARAMETER) | Used for method parameters |
@Target(ElementType.CONSTRUCTOR) | Used to construct parameters |
@Target(ElementType.LOCAL_VARIABLE) | For local variables |
@Target(ElementType.ANNOTATION_TYPE) | Used for annotations |
@Target(ElementType.PACKAGE) | For package |
With @Override For example , It is not difficult to see that its goal is method :
@Target(ElementType.METHOD)
public @interface Override {
}
Up to now , adopt @interface,@Retention,@Target You can define an annotation completely , Look at @Override Complete definition :
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- 1.1.4. @Inherited
By default , Our custom annotation used on the parent class will not be inherited by the child class . If you want the subclass to inherit the comments of the parent , That is, the annotation also works in subclasses , You need to set... When you customize annotations @Inherited. Generally, this annotation is less used .
- 1.1.5. @Documented
This annotation is used to describe other types of annotation Should be javadoc Documentation , Appear in the api doc in .
For example, using this annotation @Target It will appear in api In the description .
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
With the help of @Interface,@Target,@Retention,@Inherited,@Documented These five meta annotations , We can customize the annotation , The first three annotations are necessary for any annotation .
Do you think the following will be directly about how to customize annotations ? No , You are wrong , Let's talk java A few notes that come with you .
2. System notes
java The designer has customized several commonly used annotations for us , We call it system annotation , Mainly these three :
System notes | meaning |
@Override | Used to decorate methods , Indicates that this method overrides the parent method |
@Deprecated | Used to decorate methods , Indicates that this method is outdated |
@SuppressWarnnings | This annotation is used to tell the compiler to ignore certain compilation warnings |
If you already fully know the purpose of these three , Skip this section , Look straight down .
- 2.1. @Override
It is used as a dimensioning method , Note that the marked method overrides the method of the parent class , Its function is similar to assertion . If you use this annotation on a method that does not override the parent method ,java The compiler will prompt with a compilation error :
- 2.2. @Deprecated
When a type or member uses this annotation, it means
The compiler does not recommend that developers use marked elements . in addition , This annotation has ” Transitivity ”, Methods that override the annotation tag in subclasses , Although the method in the subclass does not use the annotation , But the compiler still alarms .
public class SimpleCalculator {
@Deprecated
public int add(int x, int y) {
return x+y;
}
}
public class MultiplCalculator extends SimpleCalculator {
// rewrite SimpleCalculator Medium method , But don't use @Deprecated
public int add(int x, int y) {
return Math.abs(x)+Math.abs(y);
}
}
//test code
public class Main {
public static void main(String[] args) {
new SimpleCalculator().add(3, 4);
new MultiplCalculator().add(3,5);
}
}
For things like new SimpleCalculator().add(3,4) This kind of direct call ,Idea Will prompt directly , The second kind is not a direct prompt :
But during compilation , The compiler will warn :
We need to pay attention to @Deprecated and @deprecated The difference between the two , The former is javac Identify and deal with , The latter is by javadoc Tool identification and handling . Therefore, when we need to mark a method in the source code as obsolete, we should use @Deprecated, If it needs to be explained in the document, use @deprecated, So it can be so :
public class SimpleCalculator {
/**
* @param x
* @param y
* @return
*
* @deprecated deprecated As of version 1.1,
* replace by <code>SimpleCalculator.add(double x,double y)</code>
*/
@Deprecated
public int add(int x, int y) {
return x+y;
}
public double add(double x,double y) {
return x+y;
}
}
- 2.3. @SuppressWarnning
Annotations are used to selectively close compiler pair classes , Method , Member variables are warnings about variable initialization . This annotation accepts the following parameters :
Parameters | meaning |
deprecated | Use obsolete classes , Method , Variable |
unchecked | Warnings when unchecked notifications are executed , For example, using a collection is to use generics to specify the type of collection when it is saved |
fallthrough | Use switch, But no break when |
path | Class path , There is a nonexistent path in the source file path |
serial | Missing on serializable class serialVersionUID Warning when defining |
finally | whatever finally Warning when words cannot be completed normally |
all | Warnings for all of the above |
3. Custom annotation
After understanding the system annotations , Now we can define annotations by ourselves , Pass above @Override Example , It is not difficult to see that the format of defining annotations is as follows :
public @interface Annotation name { Definable body }
A definition body is a collection of methods , Each method actually declares a configuration parameter . Method as the name of the configuration parameter , The return value type of a method is the type of the configuration parameter . It's not the same as the normal way , Can pass default Keyword to declare the default values of configuration parameters .
We need to pay attention to :
1. Only... Can be used here public Or by default defalt Two permission modifiers
2. The type of configuration parameter can only use basic type (byte,boolean,char,short,int,long,float,double) and String,Enum,Class,annotation
3. For annotations with only one configuration parameter , Parameter name is suggested to be set value, The method is called value
4. Once the configuration parameters are set , Its parameter value must have definite value , Or specify... When using annotations , Or use it when defining annotations default Set the default value for it , For parameter values of non basic types , It cannot be null.
image @Override such , Annotations without member definitions are called tag annotations .
Now let's define an annotation @UserMeta, This annotation is useless at present , Just to demonstrate :
@Documented
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserMeta {
public int id() default 0;
public String name() default "";
public int age() default ;
}
With rice , I can't eat without chopsticks ( Walk away with your fingers ), Let's see how to handle annotations .
Annotation processor
We've learned how to define annotations above , To make annotations work , We need to write the corresponding annotation processor for annotations . According to the nature of annotations , Annotation processor Can be divided into :1 Runtime annotation processing and 2 Compile time annotation processor . Runtime processor need With the help of reflection mechanism Realization , and Compile time processor You need to With the help of APT To achieve .
Whether it's a runtime annotation processor or a compile time annotation processor , The main task is to read annotations and process specific annotations , From this point of view, the annotation processor is very easy to understand .
Let's first look at how to write a runtime annotation processor .
- 3.1. Runtime annotation processor
be familiar with java The students of reflection mechanism must be right java.lang.reflect The bag is very familiar with , All of the api Both support reading runtime Annotation The ability of , That is, the attribute is @Retention(RetentionPolicy.RUNTIME) Annotations .
stay java.lang.reflect Medium AnnotatedElement The interface is the interface of all program elements (Class,Method) The parent interface , We can get the... Of a class by reflection AnnotatedElement object , And then you can access it through the methods provided by the object Annotation Information , The common methods are as follows :
Method | meaning |
<T extends Annotation> T getAnnotation(Class<T> annotationClass) | Return the annotation of the specified type that exists on this element |
Annotation[] getAnnotations() | Returns all annotations that exist on the element |
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) | Returns the annotation of the specified type of the element |
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) | Returns all comments that exist directly on the element |
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) | Returns a comment of a type that exists directly on the element |
Annotation[] getDeclaredAnnotations() | Returns all comments that exist directly on the element |
Writing runtime annotations generally requires understanding the above knowledge points , Now let's do a small experiment .
3.1.1. A simple example
First, we use a simple example to introduce how to write a runtime annotation processor : There is one in our system User Entity class :
public class User {
private int id;
private int age;
private String name;
@UserMeta(id=1,name="dong",age = 10)
public User() {
}
public User(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
//... Omit setter and getter Method
@Override
public String toString() {
return "User{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
We hope to pass @UserMeta(id=1,name="dong",age = 10)
( This annotation we mentioned above ) To set User The default value of the instance .
The custom annotation classes are as follows :
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
public @interface UserMeta {
public int id() default 0;
public String name() default "";
public int age() default 0;
}
This annotation class acts on the constructor , And exist at runtime , In this way, we can get annotations through reflection at runtime and then User Instance set value , See how to handle this annotation .
Runtime annotation processor :
public class AnnotationProcessor {
public static void init(Object object) {
if (!(object instanceof User)) {
throw new IllegalArgumentException("[" + object.getClass().getSimpleName() + "] isn't type of User");
}
Constructor[] constructors = object.getClass().getDeclaredConstructors();
for (Constructor constructor : constructors) {
if (constructor.isAnnotationPresent(UserMeta.class)) {
UserMeta userFill = (UserMeta) constructor.getAnnotation(UserMeta.class);
int age = userFill.age();
int id = userFill.id();
String name = userFill.name();
((User) object).setAge(age);
((User) object).setId(id);
((User) object).setName(name);
}
}
}
}
Test code :
public class Main {
public static void main(String[] args) {
User user = new User();
AnnotationProcessor.init(user);
System.out.println(user.toString());
}
}
Run test code , Then we get the result we want :
User{id=1, age=10, name=’dong’}
Here we get it through reflection User Constructor of class declaration , And check whether @UserMeta annotation . Then get the parameter value from the annotation and assign it to User object .
As mentioned above , The essence of runtime annotation processor is to get annotation information through reflection , Then do the rest . Compiling a runtime annotation processor is as simple as that . Runtime annotations are usually used for parameter configuration class modules .
- 3.1.2. Do it yourself ButterKnife
To engage in Android For development partners ,ButterKnife It can be said to be a powerful weapon , It can greatly reduce our writing findViewById(XXX). Now? , We will use the runtime annotation processor just learned to write a simplified version ButterKnife.
Custom annotation :
// This annotation is used to configure layout resources
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
int value();// When there is only one return , You can use value Make a name , In this way, you don't need to use the name to mark when using
}
// This annotation is used to configure the control ID
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
int id();
boolean clickable() default false;
}
Custom runtime annotations :
public class ButterKnife {
//view Control
public static void initViews(Object object, View sourceView){
// Get the member variables declared by this class
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields){
// Get the ViewInject annotation
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if(viewInject != null){
int viewId = viewInject.id();// obtain id Parameter values
boolean clickable = viewInject.clickable();// obtain clickable Parameter values
if(viewId != -1){
try {
field.setAccessible(true);
field.set(object, sourceView.findViewById(viewId));
if(clickable == true){
sourceView.findViewById(viewId).setOnClickListener((View.OnClickListener) (object));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
// Layout resources
public static void initLayout(Activity activity){
Class<? extends Activity> activityClass = activity.getClass();
ContentView contentView = activityClass.getAnnotation(ContentView.class);
if(contentView != null){
int layoutId = contentView.value();
try {
// Reflection execution setContentView() Method
Method method = activityClass.getMethod("setContentView", int.class);
method.invoke(activity, layoutId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void init(Activity activity) {
initLayout(activity);
initViews(activity,activity.getWindow().getDecorView());
}
}
Test code :
@ContentView(id=R.layout.activity_main)
public class MainActivity extends Activity implements View.OnClickListener {
@ViewInject(id=R.id.tvDis,clickable = true)
private TextView tvDis;
@ViewInject(id=R.id.btnNew,clickable =true)
private Button btnNew;
@ViewInject(id =R.id.btnScreenShot,clickable = true)
private Button btnScreenShot;
@ViewInject(id =R.id.imgContainer)
private ImageView imgContainer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AnnotationUtil.inJect(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tvDis:
break;
case R.id.btnNew:
break;
case R.id.btnScreenShot:
break;
}
}
}
A simple ButterKnife And that's what happened , Is it very simple . Let's move on to the most important point of this article : Compile time annotation processor .
3.2. Compile time annotation processor
Unlike the runtime annotation processor , Write compile time annotation processor (Annotation Processor Tool).
APT Used to scan and process annotation information at compile time . A specific annotation processor can be java Source files or compiled class File as input , And then output some other files , It can be .java file , It can also be .class file , But usually what we output is .java file .( Be careful : It's not modifying the source file ). If the output is .java file , these .java File round other source files are javac compile .
You may wonder , At what stage does the annotation processor intervene ? ok , In fact javac Before you start compiling , That's why we're usually willing to export .java The reason for the document .
Annotations were first made in java 5 introduce , It mainly includes apt and com.sum.mirror In the package mirror api, here apt and javac They are independent . from java 6 Start , Annotation processors are formally standardized ,apt Tools are also directly integrated into javac among .
Let's return to the topic of how to write compile time annotation processors , Compiling a compile time annotation is mainly divided into two steps : 1. Inherit AbstractProcessor, Implement your own annotation processor
2. Register processor , And beat it into jar package
It looks simple, doesn't it ? Let's take a look at the relevant knowledge points slowly .
- 3.2.1. Custom annotation handler
Let's start with a standard annotation processor format :
public class MyAnnotationProcessor extends AbstractProcessor {
@Override
public Set<String> getSupportedAnnotationTypes() {
return super.getSupportedAnnotationTypes();
}
@Override
public SourceVersion getSupportedSourceVersion() {
return super.getSupportedSourceVersion();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
}
Let's simply understand 5 The function of two methods
Method | effect |
init(ProcessingEnvironment processingEnv) | This method is automatically called by the annotation processor , among ProcessingEnvironment Class provides many useful tool classes :Filter,Types,Elements,Messager etc. |
getSupportedAnnotationTypes() | The set of strings returned by this method indicates that the processor is used to process those annotations |
getSupportedSourceVersion() | This method is used to specify the supported java edition , Generally speaking, we support the latest version , So go straight back SourceVersion.latestSupported() that will do |
process(Set annotations, RoundEnvironment roundEnv) | This method is the main place for annotation processor to process annotations , We need to write code here to scan and process annotations , And the resulting java file . What needs to be deepened is RoundEnvironment class , This is used to find the annotations used on the program elements |
To write an annotation processor, you first need to ProcessingEnvironment and RoundEnvironment very familiar . Next, let's take a look at these two classes . So let's look at this first ProcessingEnvironment class :
public interface ProcessingEnvironment {
Map<String,String> getOptions();
//Messager Used to report errors , Warnings and other reminders
Messager getMessager();
//Filter To create a new source file ,class Documents and supporting documents
Filer getFiler();
//Elements Contains information for manipulating Element The tool method of
Elements getElementUtils();
//Types Contains information for manipulating TypeMirror The tool method of
Types getTypeUtils();
SourceVersion getSourceVersion();
Locale getLocale();
}
Let's focus on Element,Types and Filer.Element( Elements ) What is it? ?
- Element
element Represents a static , Language level components . And any structured document can be seen as composed of different element The structure of the composition , such as XML,JSON etc. . Here we use XML For example :
<root>
<child>
<subchild>.....</subchild>
</child>
</root>
This paragraph xml It contains three elements :<root>,<child>,<subchild>
, By now you have understood what elements are . about java In terms of source files , It's also a structured document :
package com.closedevice; //PackageElement
public class Main{ //TypeElement
private int x; //VariableElement
private Main(){ //ExecuteableElement
}
private void print(String msg){ // The parameter part String msg by TypeElement
}
}
about java In terms of source files ,Element Represents program elements : package , class , Methods are all program elements . In addition, if you are interested in web page parsing tools jsoup be familiar with , You will feel like operating here element It's very easy , About jsoup Not covered in this article .
Next, let's look at various Element Diagram of the relationship between , In order to have a general understanding :
Elements | meaning |
VariableElement | Representing one Field , Enumeration constants , Method or parameters of construction method , Local variables and Elements such as exception parameters |
PackageElement | Represents package elements |
TypeElement | Represents a class or interface element |
ExecutableElement | Code method , Constructors , Elements such as initialization code blocks of classes or interfaces , It also includes annotation type elements |
- TypeMirror
We also need to focus on these three categories :
DeclaredType Represents the declaration type : Class type or interface type , Of course, parametric types are also included , such as Set<String>, It also includes primitive types
TypeElement Represents a class or interface element , and DeclaredType Represents a class type or interface type .
TypeMirror representative java Types in language .Types Including basic types , Declaration type ( Class types and interface types ), Array , Type variables and null types . It also represents the general configuration type parameter , The type of signature and executable file returned .TypeMirror The most important thing about classes is getKind() Method , This method returns TypeKind type , For your convenience , The source code is attached here :
public enum TypeKind {
BOOLEAN,BYTE,SHORT,INT,LONG,CHAR,FLOAT,DOUBLE,VOID,NONE,NULL,ARRAY,DECLARED,ERROR, TYPEVAR,WILDCARD,PACKAGE,EXECUTABLE,OTHER,UNION,INTERSECTION;
public boolean isPrimitive() {
switch(this) {
case BOOLEAN:
case BYTE:
case SHORT:
case INT:
case LONG:
case CHAR:
case FLOAT:
case DOUBLE:
return true;
default:
return false;
}
}
}
Simply speaking ,Element For source code ,TypeElement Represents the type element in the source code , Such as class . Although we can from TypeElement Get the class name ,TypeElement Does not contain information about the class itself , For example, its parent class , To get this information, you need to use TypeMirror, Can pass Element Medium asType() Get the corresponding TypeMirror.
And let's see RoundEnvironment, This class is relatively simple , A stroke of ink leads to :
public interface RoundEnvironment {
boolean processingOver();
// Whether the annotation processor generated an error in the last round
boolean errorRaised();
// Returns the root element generated by the annotation processor in the last round
Set<? extends Element> getRootElements();
// Returns a collection containing elements of the specified annotation type
Set<? extends Element> getElementsAnnotatedWith(TypeElement a);
// Returns a collection containing elements of the specified annotation type
Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a);
}
- Filer
Filer Used to create new files in the annotation processor . The specific usage will be demonstrated in the following examples . In addition, due to Filer It's really troublesome to use , We'll use it later javapoet Simplify our operations .
Okay , About AbstractProcessor We have read some important knowledge points in . Suppose you have written an annotation processor by now , below , What to do ?
- Package and register .
How can a custom processor take effect ? In order to make java The compiler may find a custom annotation processor, which we need to register and package : Custom processor Need to be Make it one jar, And need to be in jar Bag META-INF/services Create a fixed file in the path javax.annotation.processing.Processor, stay javax.annotation.processing.Processor In file You need to fill in the Full pathname , Yes Several processors Need Fill in a few .
from java 6 after , We just need to play jar Prevent... To the project buildpath Then you can ,javac In the process of running, it will automatically check javax.annotation.processing.Processor Registered annotation processor , And register it . and java 5 It needs to be used alone apt Tools ,java 5 It must be used less , Just skip .
Up to now , Some concepts related to annotation processors have been introduced in general , Finally, we need to get an annotation processor code jar package .
Next , To practice .
- Simple example
Use a simple example , To demonstrate how to Gradle To create a compile time annotation processor , For convenience , Here is the direct help Android studio. Of course, you can also use maven structure .
First create AnnotationTest engineering , Create apt moudle. We need to pay attention to ,AbstractProcessor Is in javax In bag , and android The package does not exist in the core library , So choose to create moudle You need to choose java Library:
At this time, the project structure is as follows :
Next, we are apt Create annotation and processor subpackage , among annotation It is used to store our customized annotations , and processor It is used to store our customized annotation processor .
Let's have a simple one first , Customize @Print annotation : The final function of this annotation is to output the annotated elements :
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface Print {
}
Next, write an annotation processor for it :
public class PrintProcessor extends AbstractProcessor {
private Messager mMessager;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mMessager = processingEnvironment.getMessager();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (TypeElement te : annotations) {
for (Element e : roundEnv.getElementsAnnotatedWith(te)) {//find special annotationed element
print(e.toString());//print element
}
}
return true;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
LinkedHashSet<String> annotations = new LinkedHashSet<>();
annotations.add(Print.class.getCanonicalName());
return super.getSupportedAnnotationTypes();
}
private void print(String msg) {
mMessager.printMessage(Diagnostic.Kind.NOTE, msg);
}
}
Now we have finished a simple annotation . In the compilation phase , The compiler will output the information of the annotated element . Because we are Gradle In the environment , Therefore, this information will be in Gradle Console Lower output .
Next, let's write a slightly difficult annotation @Code: This annotation will generate a class in the specified format , Let's first look at the definition of this annotation :
@Retention(CLASS)
@Target(METHOD)
public @interface Code {
public String author();
public String date() default "";
}
Next , We need to write annotation processors for it , Simple code , Look directly at :
public class CodeProcessor extends AbstractProcessor {
private final String SUFFIX = "$WrmRequestInfo";
private Messager mMessager;
private Filer mFiler;
private Types mTypeUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mMessager = processingEnvironment.getMessager();
mFiler = processingEnvironment.getFiler();
mTypeUtils = processingEnvironment.getTypeUtils();
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
LinkedHashSet<String> annotations = new LinkedHashSet<>();
annotations.add(Code.class.getCanonicalName());
return annotations;
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
for (Element e : roundEnvironment.getElementsAnnotatedWith(Code.class)) {//find special annotationed element
Code ca = e.getAnnotation(Code.class);
TypeElement clazz = (TypeElement) e.getEnclosingElement();
try {
generateCode(e, ca, clazz);
} catch (IOException x) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
x.toString());
return false
}
}
return true;
}
//generate
private void generateCode(Element e, Code ca, TypeElement clazz) throws IOException {
JavaFileObject f = mFiler.createSourceFile(clazz.getQualifiedName() + SUFFIX);
mMessager.printMessage(Diagnostic.Kind.NOTE, "Creating " + f.toUri());
Writer w = f.openWriter();
try {
String pack = clazz.getQualifiedName().toString();
PrintWriter pw = new PrintWriter(w);
pw.println("package " + pack.substring(0, pack.lastIndexOf('.')) + ";"); //create package element
pw.println("\n class " + clazz.getSimpleName() + "Autogenerate {");//create class element
pw.println("\n protected " + clazz.getSimpleName() + "Autogenerate() {}");//create class construction
pw.println(" protected final void message() {");//create method
pw.println("\n//" + e);
pw.println("//" + ca);
pw.println("\n System.out.println(\"author:" + ca.author() + "\");");
pw.println("\n System.out.println(\"date:" + ca.date() + "\");");
pw.println(" }");
pw.println("}");
pw.flush();
} finally {
w.close();
}
}
}
The core content is generateCode() In the method , This method utilizes the above mentioned Filer To write the source file . You'll find that , Here is mainly the process of creating character splicing , It's too much trouble .
Up to now , We have written two annotations and their corresponding processors . Now we only need to configure it .
stay resources Create under resource folder META-INF.services, Then create a file named javax.annotation.processing.Processor The file of , Configure the annotation processor that needs to be enabled in this file , That is, write the full path of the processor , Just write a few processors , Branch write Yao , For example, here we are :
com.closedevice.processor.PrintProcessor
com.closedevice.processor.CodeProcessor
Now we are ready for packing , At this time, the project structure is as follows :
Next we need to change apt moudle become involved jar package . No matter what platform you are on , Finally hit jar Even if the bag is half done . For the convenience of demonstration , Direct visualization :
Take a look at apt.jar Structure :
The following will apt.jar Copy the file to the master moudle app Under the libs In the folder , Start using it . We are simply in MainActivity.java Use :
public class MainActivity extends AppCompatActivity {
@Override
@Print
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
process();
}
@Code(author = "closedevice",date="20161225")
private void process() {
}
}
Respectively in onCreate() and process() Methods using our annotations , Now compile app modular , During the compilation process, you can Gradle Console See the output information , If nothing else , You can see the information :
In addition to app moudle Of build/intermediates/classes/debug/com/closedevice/annotationtest You can see the automatically generated MainActivityAutogenerate.class 了 . Of course, you can also directly view the source code files generated during the compilation stage com/closedevice/annotationtest/MainActivity$WrmRequestInfo.java
Take another look at the automatically generated source code :
package com.closedevice.annotationtest;
class MainActivityAutogenerate {
protected MainActivityAutogenerate() {}
protected final void message() {
//process()
//@com.closedevice.annotation.Code(date=20161225, author=closedevice)
System.out.println("author:closedevice");
System.out.println("date:20161225");
}
}
Deploy the project to our simulator , No accident , You will see the following log information :
That's it , A simple compile time annotation processor is implemented . Above, we use the runtime annotation processor to do a simple ButterKnife, But really ButterKnife Is to use compilation is to use APT To achieve , Limited to space , There will be no demonstration in this section
summary
This paper introduces the runtime annotation processor and compile time annotation processor , But about APT The content of the article is by no means clear , I will introduce it step by step later APT Knowledge about .
边栏推荐
- esp8266使用TF卡并读写数据(基于arduino)
- CDZSC_2022寒假个人训练赛21级(2)
- ViewPager2和VIewPager的区别以及ViewPager2实现轮播图
- js逆向教程第二发-猿人学第一题
- [bw16 application] Anxin can realize mqtt communication with bw16 module / development board at instruction
- C# Socke 服务器,客户端,UDP
- H5网页播放器EasyPlayer.js如何实现直播视频实时录像?
- 第一讲:包含min函数的栈
- 20排位赛3
- golang select机制和超时问题怎么解决
猜你喜欢
[4g/5g/6g topic foundation -147]: Interpretation of the white paper on 6G's overall vision and potential key technologies -2-6g's macro driving force for development
What development models did you know during the interview? Just read this one
使用BigDecimal的坑
H5网页播放器EasyPlayer.js如何实现直播视频实时录像?
4、 Fundamentals of machine learning
Lesson 1: finding the minimum of a matrix
小程序实现页面多级来回切换支持滑动和点击操作
Natapp intranet penetration
【无标题】
EXT2 file system
随机推荐
Detailed explanation of diffusion model
Dynamics 365online applicationuser creation method change
Selenium+bs4 parsing +mysql capturing BiliBili Tarot data
Nested (multi-level) childrn routes, query parameters, named routes, replace attribute, props configuration of routes, params parameters of routes
ViewPager2和VIewPager的區別以及ViewPager2實現輪播圖
Basic use of JMeter to proficiency (I) creation and testing of the first task thread from installation
面试被问到了解哪些开发模型?看这一篇就够了
golang select机制和超时问题怎么解决
H5 web player easyplayer How does JS realize live video real-time recording?
Application of C # XML
Create an int type array with a length of 6. The values of the array elements are required to be between 1-30 and are assigned randomly. At the same time, the values of the required elements are diffe
CDZSC_2022寒假个人训练赛21级(2)
VSCode+mingw64
小程序实现页面多级来回切换支持滑动和点击操作
Software modeling and analysis
PostgreSQL创建触发器的时候报错,
章鱼未来之星获得25万美金奖励|章鱼加速器2022夏季创业营圆满落幕
如何成为一名高级数字 IC 设计工程师(1-6)Verilog 编码语法篇:经典数字 IC 设计
Oracle installation enhancements error
Sword finger offer II 107 Distance in matrix