Creating an AndroidAnnotations plugin - WonderCsabo/androidannotations GitHub Wiki

Before creating a plugin, ask the AndroidAnnotations developers first. We may want to include your new annotation(s) in AA core.

Creating an AndroidAnnotations plugin

Since AndroidAnnotations 4.0.0, you can easily add new annotations by creating plugins. Please note that plugins can only allow to add new annotations, not to modify behaviour of existing ones, or add new parameters to them.

Structure

A typical plugin will consist of two modules, just like AndroidAnnotations core:

  • an API jar, which contains the annotations,
  • the processor jar, which will process the annotations and generate the code:
    • depend on the API jar
    • depend on org.androidannotations:androidannotations

You can create these with the build system of your choice, but you may want to look at one of AA's core plugins as an example.

You must choose a name for your plugin, which should be unique, so it can be found in the classpath without problems.

In these modules, you have to include some meta-data so the main AndroidAnnotations processor can find and invoke your plugin:

  • in the API jar, add a resource named {pluginname}-api.properties (like this),
  • in the processor jar, add a resource named {pluginname}.properties (like this).

Substitute pluginname with the real name of your plugin. Both of these files should have this content:

version=YOUR_VERSION

Substitute YOUR_VERSION with the real version of your plugin, which should be for example the Maven version of the artefact.

Create the service descriptor file in your processor module, named META-INF/services/org.androidannotations.plugin.AndroidAnnotationsPlugin, like this. It should contain one line, the fully qualified class name of your plugin class:

com.example.YourPlugin

Creating your annotations

In the source folder of the api module, you can create your annotations. A source file for an annotation should look like this one:

/**
 * Overrides toString() in the generated class.
 */
@Retention(RetentionPolicy.CLASS) // required
@Target(ElementType.TYPE) // this can vary per annotation
public @interface ToString {

}

Creating your processor

First, you have to create the entry point class of your plugin, which should inherit from AndroidAnnotationsPlugin:

package com.example;

import java.util.ArrayList;
import java.util.List;

import org.androidannotations.AndroidAnnotationsEnvironment;
import org.androidannotations.handler.AnnotationHandler;
import org.androidannotations.plugin.AndroidAnnotationsPlugin;

// the same class declared in the service descriptor file
public class YourPlugin extends AndroidAnnotationsPlugin {
	@Override
	public String getName() {
		return "pluginname"; // the same name used in your .properties files
	}

	@Override
	public List<AnnotationHandler<?>> getHandlers(AndroidAnnotationsEnvironment androidAnnotationEnv) {
		List<AnnotationHandler<?>> handlers = new ArrayList<>();
		handlers.add(new ToStringHandler(androidAnnotationEnv));
		// all the handlers which process your annotations
		return handlers;
	}
}

Then you can create the actual handler class which will validate and process your annotation:

package com.example;

import javax.lang.model.element.Element;

import org.androidannotations.AndroidAnnotationsEnvironment;
import org.androidannotations.ElementValidation;
import org.androidannotations.handler.BaseAnnotationHandler;
import org.androidannotations.holder.EComponentHolder;

import com.example.api.ToString;
import com.helger.jcodemodel.JExpr;
import com.helger.jcodemodel.JMethod;
import com.helger.jcodemodel.JMod;

public class ToStringHandler extends BaseAnnotationHandler<EComponentHolder> {

	public ToStringHandler(AndroidAnnotationsEnvironment environment) {
		super(ToString.class, environment); // this handles your @ToString annotation
	}

	@Override
	protected void validate(Element element, ElementValidation validation) {
		validatorHelper.enclosingElementHasEnhancedComponentAnnotation(element, validation);
		// the annotation only can be used in an enhanced class
	}

	@Override
	public void process(Element element, EComponentHolder holder) throws Exception {
		JMethod toString = holder.getGeneratedClass().method(JMod.PUBLIC, getClasses().STRING, "toString");

		toString.body()._return(JExpr.lit("Hello, AndroidAnnotations!"));
		toString.annotate(Override.class);

		// creates a method in the generated class:
		// @Override
		// public String toString() {
		//   return "Hello, AndroidAnnotations!";
		// }
	}
}

And that is it! With this minimal setup, you can process this class:

@EActivity
@ToString // your annotation
public class MyActivity extends Activity {

}

And you will generate this:

public class MyActivity_ extends MyActivity {

  @Override
  public String toString() {
    return "Hello, AndroidAnnotations!";
  }

  // ...
}

Of course this is just a dummy example. For more complicated cases, please look at the source code of AndroidAnnotations.