Ok, everything is ready — Let’s create the Annotation Processor finally.

This is the core of an annotation processor:

As you can see we annotated the class above with @AutoService in order to generate the required metadata.

Then in init(ProcessingEnvironment) we initialized the fields with Filer (for creating files) and Messager (for printing messages to console).

It’s good practice to return SourceVersion.latestSupported() in getSupportedSourceVersion().

From getSupportedAnnotationTypes() you need to return a Set of all annotation names you want to be notified about (right now we are going to register only for “eu.f3rog.log.Logged”). If you want, you can register for all annotations by simply using “*”.

And process(Set<? extends TypeElement>, RoundEnvironment) contains the logic you want to do when an annotation you registered for is found. This method can be called more than once, depending on whether some annotation processors created a class containing annotation you are registered for (RoundEnvironment has method processingOver() that you can use to determine if the current call is the last). It is important to return false (if you return true other processors won’t be notified about these annotations).

In this example I’ll put the code inside one class for simplicity, but be a good citizen and structure your code when creating your own processor ;)

What exactly will this processor do? Let’s write it out:

Get all methods that are annotated with @Logged. Divide them into groups based on the class that contains them. For each class that contains annotated methods, generate new class that will contain methods with logging — one for each annotated method.

Finding and dividing methods into groups is a pretty straight forward task:

Creating a new class requires a little bit more code, but thanks to JavaPoet and it’s nice API it’s really simple and the only thing you need to be concerned about is how to get the information you need for the generated code. I won’t go into detail of how to use JavaPoet because they have a very helpful README on their github page.

What I would like to mention is that an annotation processor sees everything as an Element. You can ask for all elements that have a specific annotation. Depending on the annotation’s @Target, you can get TypeElement (class, interface), VariableElement (field, parameter) or ExecutableElement (method). From there you can access it’s name, type, enclosed elements, element that encloses this one, etc.

Classes that are Android specific can be referenced via ClassName.get(package, className) (as I did in the example) or you can use this dependency: