Annotation processing in android
-
Upload
zhe-hao-hu -
Category
Engineering
-
view
22 -
download
0
Transcript of Annotation processing in android
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
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 { ......}
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 - 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
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'}
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/