Associated content:

Summary of basic Concepts of Java annotations

RUNTIME annotations for custom annotations (retentionPolicy.runtime)

SOURCE code annotations for custom annotations (retentionPolicy.source)

When it comes to compile-time annotations (retentionPolicy.class), it’s all about the Annotation Processor, because that’s where compile-time annotations really come in. It is important to note that RUNTIME annotations (retentionPolicy.runtime) and SOURCE annotations (retentionPolicy.source) can also be processed in the annotation handler. Different annotations have their own life cycle, depending on what you are actually using.

Annotation Processor

First, let’s look at what an Annotation handler is. An Annotation handler is a javAC tool that scans and processes annotations at compile time. You can customize annotations and register them with the annotation handler that handles your annotations. An annotation processor that takes Java code (or compiled bytecode) as input and generates a file (usually a.java file) as output. The generated Java code is in a generated.java file, so you cannot modify existing Java classes, such as adding methods to existing classes. These generated Java files are compiled by JavAC just like any other plain, hand-written Java source code.

Custom annotations (retentionPolicy.class)

Annotations (Annotations) To define the annotations to use, I’ve created a Java library for annotations, called Annotations, separate from the annotations handler I’m going to create below. The annotation library specifies JDK version 1.7, how to specify down. Custom annotations are as follows:

*/ @retention (retentionPolicy.class) @target (elementType.type) public @interface MyAnnotation {String value(); }Copy the code

Defines compile-time annotations, objects as classes or interfaces, etc.

defineAnnotation processor

So let’s define the annotations processor and create a Java library named: Processors, separate from the annotations library. Note that this must be a Java library, otherwise the related resources under the Javax package will not be found. Take a look at the current directory structure:



Here we define an annotation handler
MyProcessorEvery processor is inherited from
AbstractProcessor“And asked that it must be copied
process()Method, we usually use to copy the following four methods:

/** * Every annotation handler class must have an empty constructor. */ public class MyProcessor extends AbstractProcessor {/** * init() is called by the annotation processing tool with the ProcessingEnviroment parameter. * ProcessingEnviroment provides a number of useful utility classes Types and Filer * @param processingEnv provide the environment that the processor uses to access the tool framework */ @override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); } /** * This is equivalent to the main function () on each processor, where you write your code to scan, evaluate, and process annotations, as well as generate Java files. * Enter the parameter RoundEnviroment, Annotations allow you to query for annotated elements that contain specific annotations. @Param Annotations Specifies the type of annotations that the request handles. These annotations are declared and do not require subsequent processors to process them; * If false is returned, Annotations */ @override public Boolean process(Set Annotations, Annotations) RoundEnvironment roundEnv) { return false; } /** * you must specify which annotation handler is registered for. Note that its return value is a collection of strings containing the legal full name of the annotation type that this processor wants to process. * @return Annotator supports a collection of annotation types. If there is no such type, It returns an empty Set * / @ Override public Set getSupportedAnnotationTypes () {Set annotataions = new LinkedHashSet (); annotataions.add(MyAnnotation.class.getCanonicalName()); return annotataions; } / * * * specified using Java version, usually returns SourceVersion. Here latestSupported (), . The default return SourceVersion RELEASE_6 * @ the Java version of return to use * / @ Override public SourceVersion getSupportedSourceVersion () {return SourceVersion.latestSupported(); }}Copy the code

The notes above make it quite clear that the work we need to deal with is in
process()
Method. I’ll give you an example in a second. For getSupportedAnnotationTypes () method which indicate the annotation processor to deal with annotations, returns a Set value, an annotation processor can handle multiple annotations. In addition to specifying the annotations to work with in this method, you can also specify annotations (SourceVersion also) :

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.example.annotation.cls.MyAnnotation")
public class MyProcessor extends AbstractProcessor {
    // ...
}Copy the code

For compatibility reasons, especially for the Android platform, reloading is recommended
getSupportedAnnotationTypes()
getSupportedSourceVersion()Methods to replace
@SupportedAnnotationTypes
@SupportedSourceVersion

Now to add annotation processing, simply output some information, code as follows:

@Override public boolean process(Set annotations, RoundEnvironment roundEnv) {/ / roundEnv getElementsAnnotatedWith () returns using a given annotation types of elements for (Element Element: roundEnv.getElementsAnnotatedWith(MyAnnotation.class)) { System.out.println("------------------------------"); Class if (element.getkind () == elementkind.class) {TypeElement TypeElement = (TypeElement); element; / / output element names System. Out. Println (typeElement. GetSimpleName ()); / / output annotation attribute value System. Out. Println (typeElement. GetAnnotation (MyAnnotation. Class). The value ()); } System.out.println("------------------------------"); } return false; }Copy the code

Now that the annotation handler is written, let’s see how to run it.

Running annotation handler

Before you run it, you need to introduce the annotations and Processors libraries into your main project. (Introducing a Processors library is nota good idea; more appropriate methods are described below.) If you compile or run the project directly, you will not see any output. The next step is to specify where the annotation handler is.

Create a resources folder in the main directory of the Processors library.

2, inCreate META-INF/services directory in resources folder.

3, inMeta-inf/services directory folder to create javax.mail. Annotation. Processing. The Processor files;

4, inJavax.mail. The annotation. Processing. Write the full name of annotation Processor Processor file, including the package path;

Take a look at the entire directory structure:



Once we’re done, we’ll use the @myAnnotation annotation in our project:

@MyAnnotation("Hello Annotation")
public class MainActivity extends AppCompatActivity {
    // ...
}Copy the code

If you do not see the output, you should first clean the project under compilation, as follows:



The following information is displayed:



The annotation processor is now working properly

Of course, there’s a problem with that. Our main project uses the Processors library, but the annotation processor is only needed during compilation and then useless, and adding the library to the main project introduces a lot of unnecessary files. To deal with this problem we need to introduce a plugin android-apt, which can handle this problem very well.

Before introducing this plug-in, I’d like to introduce a useful library, AutoService, which has a pit in it.

AutoService

Did you have trouble specifying the annotation handler earlier? That’s all it takes to add an annotation handler, but that’s okay,AutoService can help you solve this problem.

AutoServiceThe annotation processor was developed by Google to generateMETA-INF/services/javax.annotation.processing.Processor File, you just need to add to the annotation handler you define@AutoService(Processor.class)B: That’s fine. It couldn’t be more convenient.

Add processors to the Processors library by relying on AutoService. You can search for AutoService in AndroidStudio and add it as follows:



Once added, it can be used directly on the annotation handler we defined earlier:

@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {
    // ...
}Copy the code

All in one sentence! If you re-make the project, you will see the same output. However, if you compile the APK, you will find an error as follows:



Duplicate file found! Here is a solution in the main project
build.gradleAdd the following paragraph:

apply plugin: 'com.android.application'

android {
    // ...
    packagingOptions {
        exclude 'META-INF/services/javax.annotation.processing.Processor'
    }
}Copy the code

So you don’t get any errors, so that’s one solution, but a better solution is to use the one above
android-aptAll right, here we go

android-apt

So what is Android-APT? The website has this description:

The android-apt plugin assists in working with annotation processors in combination with Android Studio. It has two purposes:

1, Allow to configure a compile time only annotation processor as a dependency, not including the artifact in the final APK or library

Set up the source paths so that code that is generated from the annotation processor is correctly picked up by Android Studio

Basically, it has two functions:

  • You can rely on the annotation processor and work at compile time, but you don’t include any legacy when you generate APK
  • To be able to assistAndroid Studio stores the files generated by the annotation handler during compilation in the corresponding directory of the project

So this is a pretty good solution to the problem that we had, so let’s see how it works.

Build. Gradle = build.gradle = build.gradle

buildscript { repositories { jcenter() mavenCentral() // add } dependencies { classpath 'com. Android. Tools. Build: gradle: 2.1.2' classpath 'com. Neenbedankt. Gradle. Plugins: android - apt: 1.8' / / add}}Copy the code

In the main project (APP)
build.gradle Add two statements to:

apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' // add // ... dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') testCompile' junit: junit: 4.12 'compile' com. Android. Support: appcompat - v7:23.4.0 'compile project (' : annotations') // compile project(':processors') instead apt project(':processors')}Copy the code

That’s it, rerun should work fine


As mentioned above, The function of Android-apt is to process files generated at compile time. As for the function of generated files, we have to mention JavaPoet, which will be covered in the next article

Compile-time annotations for custom annotations (retentionPolicy.class) (2) — JavaPoet

Source: AnnotationSample