Annotation processing in android

31
Annotation processing in Android Randy Hu @ 91 APP

Transcript of Annotation processing in android

Annotation processing in Android

Randy Hu @ 91 APP

Library use Annotation Processor

• AndroidAnnotation

• http://androidannotations.org/

• ButterKnife

• https://github.com/JakeWharton/butterknife

• Dagger

• http://square.github.io/dagger/

Library (not) use Annotation Processor

• Retrofit

• http://square.github.io/retrofit/

• galette

• https://github.com/uPhyca/GAlette

Annotation@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}

• Target• Where can annotate

• Retention• Where to store

Target• TYPE

• FIELD

• METHOD

• PARAMETER

• CONSTRUCTOR

• LOCAL_VARIABLE

• ANNOTATION_TYPE

• PACKAGE

Retention• SOURCE

• CLASS

• RUNTIME

• used in Reflection

• java.lang.reflect.AnnotatedElement

Parameters@Retention(RetentionPolicy.SOURCE)@Target(ElementType.TYPE)public @interface TypeAnnotation { String[] uris() default {}; int count() default 0; String value() default ""; AnnotationEnum enu() default AnnotationEnum.Enum1;}

Usage:

@TypeAnnotation(uris={"MainActivity", "1"}, value="a", count=20)public class MainActivity extends ActionBarActivity { ......}

Write Annotation Processor ( with your bare hand )

Modules• annotations

• Annotations can be used in app & annotation-processor

• app

• main

• processor

• process annotations annotated in app

Dependency - top level

buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.2.3' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' }}

Dependency - processor

dependencies {... compile project(':annotation')}

Dependency - app

apply plugin: 'com.android.application'apply plugin: 'android-apt'

...

...

dependencies { ... apt project(':annotationprocessor') compile project(':annotation') }

annotations@Retention(RetentionPolicy.SOURCE)@Target(ElementType.METHOD)public @interface MethodAnnotation { String parameterName(); String parameterValue();}

app@MethodAnnotation(parameterName = "input", parameterValue = "inputValue")public void stringContact(String input) {}

processor - basic setting

// Supported Annotation@SupportedAnnotationTypes({"me.lunacat.annotations.MethodAnnotation"})public class AnnotationProcessor extends AbstractProcessor { private Filer filer; private Messager messager; @Override public synchronized void init(ProcessingEnvironment env){ super.init(env); filer = processingEnv.getFiler(); // File instance messager = processingEnv.getMessager(); // Logger }

@Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); }

// processing function public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {}}

processor - gather annotations

@Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { ArrayList<MethodAnnotation> methodAnnotations = new ArrayList<>(); for (Element e : roundEnv.getElementsAnnotatedWith(MethodAnnotation.class)) { MethodAnnotation annot = e.getAnnotation(MethodAnnotation.class); methodAnnotations.add(annot); } generateJavaFile(methodAnnotations);}

processor - generate code private void generateJavaFile(ArrayList<MethodAnnotation> methodAnnotations) throws IOException { // create ExtraClass with annotation content JavaFileObject source = filer.createSourceFile(“in.test.AnnotationGenClass”); Writer writer = source.openWriter(); try { PrintWriter pw = new PrintWriter(writer); pw.println("package in.test;"); pw.println("public class AnnotationGenClass {"); for(MethodAnnotation annot: methodAnnotations) { pw.println("public static final void " + annot.parameterName() + "() {}"); } pw.println("}"); pw.flush(); pw.close(); } finally { writer.close(); }}

processor - generate resources

private void generateJavaFile(ArrayList<MethodAnnotation> methodAnnotations) throws IOException { // create method.txt with annotation contents FileObject file = filer.createResource( StandardLocation.SOURCE_OUTPUT, "", "methods.txt", null); writer = file.openWriter(); try { PrintWriter pw = new PrintWriter(writer); for(MethodAnnotation annot: methodAnnotations) { pw.println(annot.parameterName()); } pw.flush(); pw.close(); } finally { writer.close(); }}

processor - create meta-data

create file: javax.annotation.processing.Processor file content: me.lunacat.AnnotationProcessor

Write Annotation Processor ( with tools )

Generate Code - filer

private void generateJavaFile(ArrayList<MethodAnnotation> methodAnnotations) throws IOException { // create ExtraClass with annotation content JavaFileObject source = filer.createSourceFile(“in.test.AnnotationGenClass”); Writer writer = source.openWriter(); try { PrintWriter pw = new PrintWriter(writer); pw.println("package in.test;"); pw.println("public class AnnotationGenClass{"); for(MethodAnnotation annot: methodAnnotations) { pw.println("public static final void " + annot.parameterName() + "() {}"); } pw.println("}"); pw.flush(); pw.close(); } finally { writer.close(); }}

Generate Code - JavaPoet for(MethodAnnotation annot: methodAnnotations) { MethodSpec dumpMethod = MethodSpec .methodBuilder(annot.parameterName()) .addModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PUBLIC) .returns(void.class) .build(); dumpMethods.add(dumpMethod);}// create classTypeSpec.Builder classBuilder = TypeSpec .classBuilder("AnnotationGenClass") .addModifiers(Modifier.PUBLIC);if ( dumpMethods != null ) { classBuilder.addMethods(dumpMethods);}JavaFile.builder("me.lunacat.annotationgent", classBuilder.build()) .build() .writeTo(filer);

with dependenciesdependencies { compile 'com.squareup:javapoet:1.1.0'}

MetaData - bare hand

create file: java.annotation.processing.Processor file content: me.lunacat.AnnotationProcessor

MetaData - Auto

• Auto

• https://github.com/google/auto

@AutoService(Processor.class)public class AnnotationProcessor extends AbstractProcessor {}

With dependency:

dependencies { compile 'com.google.auto.service:auto-service:1.0-rc2'}

TestingJavaFileObject sampleActivity = JavaFileObjects .forSourceString("SampleActivity", "package com.example;" + "import me.lunacat.annotations.TypeAnnotation; " + "@Type(\"airbnb://example.com/deepLink\") public class SampleActivity {}”);

// sampleActivity with AnnotationProcessor with generate source likeassert_().about(javaSource()) .that(sampleActivity) .processedWith(new AnnotationProcessor()) .compilesWithoutError() .and() .generatesSources( JavaFileObjects.forResource("AnnotationGenClass.java"), JavaFileObjects.forSourceString("AnnotationGenClass", "package.."));

with dependenciesdependencies { testCompile 'com.google.testing.compile:compile-testing:0.6'}

Debug

Debugmessager.printMessage(Diagnostic.Kind.WARNING, "no annot found");

Pass Parameter to Processor

Pass Parameter to Processor

apt { arguments{ host "http://www.123456789.host/" }}

build.gradle (app)

Processor

public synchronized void init(ProcessingEnvironment env) { String host = env.getOptions().get("host");}

References• Annotation 101

• http://brianattwell.com/android-annotation-processing-pojo-string-generator/

• http://hannesdorfmann.com/annotation-processing/annotationprocessing101/#processing-rounds

• Debug Annotation Processor

• https://turbomanage.wordpress.com/2014/06/09/debug-an-annotation-processor-with-intellij-and-gradle/