Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Annotation Processing in Android

3,677 views

Published on

Talk given at DroidKaigi on February 19, 2016

Published in: Software
  • Be the first to comment

Annotation Processing in Android

  1. 1. ANNOTATION PROCESSING IN ANDROIDEmanuele Zattin - Realm @emanuelez 19-2-2016 - DroidKaigi
  2. 2. DroidKaigi
  3. 3. WHAT IS A JAVA ANNOTATION?
  4. 4. LET'S START WITH THE OFFICIAL DEFINITION Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate. — Oracle
  5. 5. WHAT CAN ANNOTATIONS DO? 1. Provide information for the compiler 2. Allow runtime processing 3. Allow compile-time processing
  6. 6. HOW TO DEFINE AN ANNOTATION The simplest annotation can be defined as: public @interface MyAnnotation {} and it can be used like this: @MyAnnotation public class MyClass {}
  7. 7. ANNOTATIONS CAN ACCEPT ARGUMENTS public @interface MyAnnotation { String arg1(); int arg2 default 1; String[] arg3; } Which can be used like this: @MyAnnotation ( arg1 = "value1", // It's a comma, not a semicolon arg3 = { "value2", "value3" } // Arrays use curly brackets ) public class MyClass {}
  8. 8. ANNOTATIONS CAN BE ANNOTATED The most important is @Retention which value can be: ▸ RetentionPolicy.SOURCE ▸ RetentionPolicy.CLASS ▸ RetentionPolicy.RUNTIME
  9. 9. ANNOTATIONS CAN BE ANNOTATED PART 2 The other important annotation is @Target which value: ElementType.ANNOTATION_TYPE ElementType.CONSTRUCTOR ElementType.FIELD ElementType.LOCAL_VARIABLE ElementType.METHOD
  10. 10. ANNOTATIONS CAN BE ANNOTATED PART 3 Other useful annotations: ▸ @Documented ▸ @Inherited ▸ @Repeatable
  11. 11. AN EXAMPLE ANNOTATION @Retention(RetentionPolicy.CLASS) // Available at compile-time @Target(ElementType.TYPE) // Can only be applied to classes @interface MyAnnotation { String arg1(); int arg2 default 1; String[] arg3; }
  12. 12. WHAT IS AN ANNOTATION PROCESSOR?
  13. 13. Annotation Processing is a technique that provides a hook into the Java compile process. It allows to produce compiler errors and warnings and to generate source code and byte code.
  14. 14. JSR 269
  15. 15. HOW DOES IT WORK? Here's a high-level example: 1. Normal compilation 2. First round of annotation processing 3. Second round of annotation processing 4. ...
  16. 16. IMPLEMENTING A PROCESSOR public abstract class AbstractProcessor implements Processor { // more methods here! void init( ProcessingEnvironment processingEnv ); abstract boolean process( Set<? extends TypeElement> annotations, RoundEnvironment roundEnv ); }
  17. 17. THE PROCESSING ENVIRONMENT It provides the tools to write new files and access utility classes. Here are the most useful methods: // Use Filer to write new files Filer getFiler(); // Use Elements to manage fields, methods and classes Elements getElementUtils(); // Use Types to deal with classes, converting Type to Element, ... Types getTypeUtils();
  18. 18. THE ROUND ENVIRONMENT It provides tools to deal with the specific round. The most useful methods are: // Get the elements annotated with a given annotation Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a); Set<? extends Element> getElementsAnnotatedWith(TypeElement a);
  19. 19. THE MOST USELESS ANNOTATION public AnnoyingProcessor extends AbstractProcessor { boolean process( Set<> annotations, RoundEnvironment env) { Messager m = processingEnv.getMessager(); for (TypeElement te : annotations) { for (Element e : env.getElementsAnnotatedWith(te)) { m.printMessage(Diagnostic.Kind.NOTE, "Processing " + e.toString()); } } return true; } }
  20. 20. REGISTERING YOUR PROCESSOR 1. Package your processor in a Jar file 2. The Jar file must contain a file called javax.annotation.processing.Proce ssor located in META-INF/services 3. This file must contain the fully qualified name of your processor
  21. 21. GENERATING JAVA CODE IN YOUR AP Enter JavaPoet
  22. 22. MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!") .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main) .build(); JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) .build(); javaFile.writeTo(processingEnv.getFiler());
  23. 23. TESTING Testing annotations processors is hard because the execution happens at compile time
  24. 24. SOME MUCH NEEDED HELP! Google's compile-testing library
  25. 25. EXAMPLE 1 assert_() // it uses the Truth testing library .about(javaSource()) .that( JavaFileObjects.forSourceString( "HelloWorld", "final class HelloWorld {}" ) ).compilesWithoutError();
  26. 26. EXAMPLE 2 assert_().about(javaSource()) .that(JavaFileObjects.forResource("HelloWorld.java")) .processedWith(new MyAnnotationProcessor()) .compilesWithoutError() .and() .generatesSources( JavaFileObjects.forResource("GeneratedHelloWorld.java"));
  27. 27. HOW ABOUT ANNOTATION PROCESSING IN ANDROID?
  28. 28. PROBLEM 1 The Android framework does not include javax.annotation.processing
  29. 29. SOLUTION Setup your AP to be a Java module and to inform your users to use the Gradle android-apt plugin by Hugo Visser
  30. 30. PROBLEM 2 Both your library and your AP might depend on the annotations
  31. 31. SOLUTION Setup your annotations to be another Java module on which both the library and the AP depend on
  32. 32. PROBLEM 3 Now the Javadoc of your library does not include the annotations
  33. 33. SOLUTION android.libraryVariants.all { variant -> task("javadoc${variant.name.capitalize()}", type: Javadoc) { description "Generates Javadoc for $variant.name." group 'Docs' source = variant.javaCompile.source source "../annotations/src/main/java" // <-- THIS! ext.androidJar = files(project.android.getBootClasspath()) classpath = files(variant.javaCompile.classpath.files) + ext.androidJar exclude '**/BuildConfig.java' exclude '**/R.java' } }
  34. 34. ANDROID LIBRARIES THAT USE AP ▸ ButterKnife ▸ Dagger ▸ Parceler ▸ Realm
  35. 35. Thank you!
  36. 36. Questions?

×