When working on a testing library in my spare time, I thought that annotations would be useful to create a graph structure the same way that Dagger does for object dependency, but I was only experienced with writing annotations that were referenced at run-time. 

So I went to the web for tutorials, blog posts, and videos on annotation processing. I was able to find enough information to set up my own annotation processor, but there wasn’t a comprehensive walkthrough on how to set it up for Android. Since annotation processing is purely Java, all the tutorials showed the processor in its own project, but I wanted my processor in the same project as my Android app so that a call to build the project would also trigger a build for the annotation processor; after all I needed this to be responsive to the tests I would create. So when I was asked what my first blog post would be, I was ready.

Annotations are a class of metadata that can be associated with classes, methods, fields, and even other annotations. This metadata can be accessed at runtime via reflection, but you can also access this metadata at build time via annotation processors. Annotation processors are a useful interface added in Java 6 that perform exhaustive searches over all annotations at build time and allow you to access reflective information regarding which element was annotated as well as any additional metadata stored in the corresponding annotation. Annotations and processors have not been deeply explored by mainstream developers until recently.

  • Jake Wharton gives a great in-depth presentation on annotation processing, its history, and its use in Dagger/Dagger2.
  • Hannes Dorfman gives a great presentation covering just the concepts of how annotation processing works.

Before we begin, let’s cover how we will be using annotation processing at a high level:

When you build and run this code, the first thing that happens is the annotation processor code is compiled into a jar via a pre-build gradle task so it can be automatically included and used in our Android project (annotation processors are pure Java modules as of RELEASE_8 and will not have access to the Android API). Next, the build process will begin processing all annotations in our Android project, including the custom one that we will write. Our annotation processor will create a generated java class object that can be used inside of our Android code at runtime. In doing so we will have proven the concept of generating code from annotations at build time and using the generated code during runtime.

The package structure is important and trying to rename or move packages to fix a mistake doesn’t always work as intended, so getting them right the first time makes life easier. The naming convention boils down to these two packages:

<base>              => com.stablekernel.annotationprocessor
<base>.processor    => com.stablekernel.annotationprocessor.processor

For this tutorial we will start with an Empty Activity default “Hello, world!” app created through the Android Studio wizard and I will have my Project pane set to Project.

1) Creating the processor module:
Since we are starting from an existing app, we will first need to create the annotation processor in its own module by either right clicking the project folder and selecting New>New Module or by going to File>New>New Module.


Because this is not an Android native feature you will need to create a Java library module, not Android.



For this tutorial, the module will be named processor.
Make sure that your package name is correct: <base>.processor
The class name is simply the first file it generates in the library, I chose to name it for the annotation processor we will be making.

2) Setting the source compatibility:
In the build.gradle file for the in the app/ directory, set the android compile options.


For this tutorial the compile options are:

compileOptions {
   sourceCompatibility JavaVersion.VERSION_1_7
   targetCompatibility JavaVersion.VERSION_1_7


And in the processor module’s build.gradle file:


Add the compatibility options:

sourceCompatibility = 1.7
targetCompatibility = 1.7


Note that while the gradle file indicates that these arguments aren’t used, they are needed during the build process.

3) Creating the annotation:
Before we get to the processor, let’s create our CustomAnnotation.class as an annotation in this new module.



For now we will leave the auto-generated annotation empty as we only care about which elements are annotated in this tutorial.


4) Creating the processor:
The processor class should extend from the AbstractProcessor class and be annotated with fully qualified paths of the annotation types that are expected to be handled (for this tutorial there is only the one) as well as the source version of Java. For this tutorial the source version is Java 7 but if your project is using Java 6 you would use RELEASE_6.

@SupportedAnnotationTypes(“<fully qualified annotation path>”)


The easiest way to get the fully qualified path of the supported annotation type is to copy it using Android Studio’s project pane.


It is important that you use a correct qualified path name and if you refactor the class later that you update this annotation otherwise the build will fail and the error tracing is not the easiest to follow.


Android Studio should now be notifying you that you need to implement the process method, so let’s do that before moving on.


Then replace the process method with the following:

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
   StringBuilder builder = new StringBuilder()
           .append("package com.stablekernel.annotationprocessor.generated;\n\n")
           .append("public class GeneratedClass {\n\n") // open class
           .append("\tpublic String getMessage() {\n") // open method
           .append("\t\treturn \"");

   // for each javax.lang.model.element.Element annotated with the CustomAnnotation
   for (Element element : roundEnv.getElementsAnnotatedWith(CustomAnnotation.class)) {
       String objectType = element.getSimpleName().toString();

       // this is appending to the return statement
       builder.append(objectType).append(" says hello!\\n");

   builder.append("\";\n") // end return
           .append("\t}\n") // close method
           .append("}\n"); // close class

   try { // write the file
       JavaFileObject source = processingEnv.getFiler().createSourceFile("com.stablekernel.annotationprocessor.generated.GeneratedClass");

       Writer writer = source.openWriter();
   } catch (IOException e) {
       // Note: calling e.printStackTrace() will print IO errors
       // that occur from the file already existing after its first run, this is normal

   return true;

To give a conceptual idea of what is happening here, the StringBuilder is creating a Java file with the package name in the generated namespace. This file is given a single method, getMessage which will return a string. That return value is being generated by finding each of the supported annotations and finding the name of the element associated with the annotation. In the case of this tutorial it will be MainActivity and onCreate as the two items annotated, so the generated file should look like this:


Note that this is a generated file, it is created during the build process so you will not be able to view it until after the project has been built successfully. For reference you will find the file after a successful build in this directory: app/build/generated/source/apt/debug/<package>/GeneratedClass.java

Also, we are writing a source file here via Writer which serves our purpose for now but more complex file writing as your project develops may be made easier by third party libraries like JavaPoet.

5) Create the resource:
Now that we have created our processor we will need to tell Java to use it by creating the javax Processor file, this is what the compiler looks for to know how to handle the annotations. The steps are a bit tedious, but this is because the processor expects a specific structure to be in place.

  1. From the processor module’s main directory, create a new directory called resources
  2. Within this directory create a new directory called META-INF
  3. Within this directory create a new directory called services
  4. Within this directory create a new file called  javax.annotation.processing.Processor


Inside this file you will put the fully qualified name of each of your processors, separated by a newline.They should autocomplete and in this instance we only have one so our file looks like this:


6) Add android-apt:
Next, apply the android-apt plugin by first updating the build.gradle file for your project:


And adding the buildscript dependency:
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'


Then in the build.gradle file in the app/ directory:


Apply the plugin:
apply plugin: 'com.neenbedankt.android-apt'


7) Set build dependencies:
The main concept of this section is that we need to compile the processor and the annotation into a jar and then put that jar into our app module and make it available for reference, but this needs to be done before the app is built. First, we will update the dependencies to look for the jar file:

dependencies {
   compile files('libs/processor.jar')
   testCompile 'junit:junit:4.12'
   compile 'com.android.support:appcompat-v7:23.1.1'

Then we will create a gradle task that will move the jar file into the libs/ folder

task processorTask(type: Exec) {
   commandLine 'cp', '../processor/build/libs/processor.jar', 'libs/'

Finally, we establish the order of dependencies, so the :app:preBuild depends on our new processorTask, which depends on :processor:build:



So the build sequence is:

  1. :processor:build will generate the jar file with our annotation and its processor
  2. processorTask will copy this jar file to the android app/libs/ folder
  3. The :app module will now reference the new jar.

To verify that all of this happens as expected perform a clean build by either going to Build>Rebuild or in terminal executing


./gradlew :app:clean :app:build

and you should see the build process run in the correct order and finish with a processor.jar file in your app/libs/ folder.


8) Apply annotations:
Now that the jar file containing the annotation and the annotation processor is in the android app we can reference the CustomAnnotation and can apply it to the MainActivity class declaration and the onCreate method.


But just applying these annotations will not create the generated class that the annotation processor is supposed to create, to do so we will need to rebuild again by going to Build>Rebuild or in terminal executing


./gradlew :app:clean :app:build

and now the generated file should be seen at this location:



9) Verify annotations are working:
In order to verify that the annotations are being processed, we will launch an alert dialog by adding the following code to MainActivity.java and call it from onCreate

private void showAnnotationMessage() {
GeneratedClass generatedClass = new GeneratedClass();
String message = generatedClass.getMessage();
            // android.support.v7.app.AlertDialog
new AlertDialog.Builder(this)
.setPositiveButton("Ok", null)
.setTitle("Annotation Processor Messages")

Note that this GeneratedClass will not exist until a build has succeeded.


10) Running the build:
Building and running on a device now produces the following.


This is the basis of annotation processing: by applying the @CustomAnnotation we are able to intercept it at build time, create a generated file, then at runtime this generated file is able to be used.


Are you an Android developer wanting to learn more? Check out "Level-up with Android Studio Shortcuts and Live Templates."


About The Author

Chris Logan is an Android developer at stable|kernel. He has worked in a polyglot capacity for several Atlanta-based companies including Tech, ranging from Web Development to OpenGL/CUDA to Chrome extensions. You can find out what he’s currently up to on his website.