Creating custom annotations in Android can greatly enhance the readability, maintainability, and robustness of your code. Annotations allow you to provide metadata to your code elements (such as methods, classes, variables), which can then be used for various purposes like code generation, validation, and more. Here’s a detailed explanation on creating and using custom annotations in Android:

To define a custom annotation, you use the @interface keyword. Here’s an example of creating a simple annotation:

// Define a custom annotation
public @interface LogExecutionTime {
  • @Retention: Specifies how long the annotation is retained. Common values are:
  • RetentionPolicy.SOURCE: Discarded during the compile phase; not available at runtime.
  • RetentionPolicy.CLASS: Recorded in the class file but not available at runtime.
  • RetentionPolicy.RUNTIME: Available at runtime, allowing reflection.
  • @Target: Indicates the kinds of program elements to which the annotation can be applied. Common values are:
  • ElementType.TYPE: Class, interface (including annotation type), or enum declaration.
  • ElementType.FIELD: Field (including enum constant).
  • ElementType.METHOD: Method.
  • ElementType.PARAMETER: Parameter.
  • ElementType.CONSTRUCTOR: Constructor.
  • ElementType.LOCAL_VARIABLE: Local variable.
  • ElementType.ANNOTATION_TYPE: Annotation type.
  • ElementType.PACKAGE: Package.

You can apply your custom annotations to the appropriate elements in your code. For example:

public class ExampleService {

public void serve() {
// Method implementation

Processing annotations typically involves using reflection or annotation processors. Here’s how you can process the @LogExecutionTime annotation using reflection:

import java.lang.reflect.Method;

public class AnnotationProcessor {

public static void processAnnotations(Object object) throws Exception {
Class<?> clazz = object.getClass();
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(LogExecutionTime.class)) {
long start = System.currentTimeMillis();
long end = System.currentTimeMillis();
System.out.println("Execution time of " + method.getName() + " is " + (end - start) + "ms");

public class Main {

public static void main(String[] args) throws Exception {
ExampleService service = new ExampleService();

When processAnnotations is called, it will find methods annotated with @LogExecutionTime and print their execution time.

Custom annotations can be powerful tools for various advanced use cases, such as:

a. Dependency Injection:

Annotations can be used to mark dependencies for injection.

public @interface Inject {

public class MyActivity extends Activity {

MyService myService;

b. Code Generation:

Annotation Processors (APT) can be used to generate code at compile-time.

public class MyAnnotationProcessor extends AbstractProcessor {

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(MyAnnotation.class)) {
// Generate code based on the annotated elements
return true;

c. Validation:

Annotations can be used for validating method parameters, fields, etc.

public @interface NotNull {

public class Validator {

public static void validate(Object object) throws IllegalAccessException {
for (Field field : object.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(NotNull.class) && field.get(object) == null) {
throw new IllegalArgumentException(field.getName() + " should not be null");

Custom annotations can be integrated with libraries like Dagger for dependency injection, Retrofit for networking, or Room for database operations.

// Dagger example
public @interface ForApplication {

public class AppModule {
Context provideApplicationContext(Application application) {
return application.getApplicationContext();

By using custom annotations effectively, you can make your Android code more modular, maintainable, and easier to understand.

Source link