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:
1. Defining Custom Annotations
To define a custom annotation, you use the @interface
keyword. Here’s an example of creating a simple annotation:
// Define a custom annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
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.
2. Using Custom Annotations
You can apply your custom annotations to the appropriate elements in your code. For example:
public class ExampleService {
@LogExecutionTime
public void serve() {
// Method implementation
}
}
3. Processing Custom Annotations
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();
method.invoke(object);
long end = System.currentTimeMillis();
System.out.println("Execution time of " + method.getName() + " is " + (end - start) + "ms");
}
}
}
}
Job Offers
4. Example Usage
public class Main {
public static void main(String[] args) throws Exception {
ExampleService service = new ExampleService();
AnnotationProcessor.processAnnotations(service);
}
}
When processAnnotations
is called, it will find methods annotated with @LogExecutionTime
and print their execution time.
5. Advanced Use Cases
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.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Inject {
}
public class MyActivity extends Activity {
@Inject
MyService myService;
}
b. Code Generation:
Annotation Processors (APT) can be used to generate code at compile-time.
@SupportedAnnotationTypes("com.example.MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
@Override
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.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
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");
}
}
}
}
6. Integrating with Libraries
Custom annotations can be integrated with libraries like Dagger for dependency injection, Retrofit for networking, or Room for database operations.
// Dagger example
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ForApplication {
}
@Module
public class AppModule {
@Provides
@ForApplication
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.