Annotation Processor

android-apt 与 AnnotationProcessor

APT(Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。(额外的处理包括,修改源文件,增加新代码,甚至添加新的Annotation)

android-apt是一个gradle插件,始于三年前 ByHugo*Visser*

AnnotationProcessor是Android Gradle 插件 2.2 版本提供的插件

能干什么?

少些代码(增加开发效率,代码看起来更优雅)

解耦(ActivityRouter)

谁都在用?

EventBus、Retrofit、Dagger2、ButterKnife等等

像官方靠齐,本文均采用AnnotationProcessor进行示范

初体验:

AnnotationProcessor 所需环境:

gradle 插件2.2 +

AndroidStudio 2.2 (AndroidStudio 2.3调试不成功,未知原因)

所需知识:

Java注解知识(https://joyrun.github.io/2016/07/18/java-annotation)JavaPoet(http://www.jianshu.com/p/95f12f72f69a)

1、Gradle插件

buildscript{repositories{        jcenter()    }dependencies{classpath'com.android.tools.build:gradle:2.2.1'}}

2、新建Annotation定义的Module

build.gradle定义如下:

apply plugin:'java'dependencies{compilefileTree(dir:'libs',include: ['*.jar'])}sourceCompatibility="1.7"targetCompatibility="1.7"

当然你也可以不新建一个Module来做,写在业务Module里,但是Compiler要依赖,所以不建议写在也业务里。

3、新建注解处理Module

build.gradle定义如下:

applyplugin:'java'dependencies{compilefileTree(include: ['*.jar'],dir:'libs')compile'com.google.auto.service:auto-service:1.0-rc2'compile'com.squareup:javapoet:1.7.0'compileproject(':annotation')}sourceCompatibility="1.7"targetCompatibility="1.7"

auto-service:可以将自定义的Compiler自动注册进去

javapoet:优雅的生成Java类的工具

compile project(':annotation') 依赖刚才新建的annotation module 因为需要引用annotation

注解处理是整个Annotation Processor的核心,对Annotation进行处理的就是这个部分。

继承Java提供的AbstractProcessor,重写相关方法就可以实现对Annotation的处理

@AutoService(Processor.class)publicclassUrlCompilerextendsAbstractProcessor{privatestaticfinalStringPACKAGE="com.baidu.appsearch.config";privatestaticfinalStringCLASSNAME_SUFFIX="Injection";@OverridepublicSet getSupportedAnnotationTypes() {returnCollections.singleton(UrlClass.class.getCanonicalName());}@Overridepublic boolean process(Setannotations, RoundEnvironment roundEnv) {for(Elementelement : roundEnv.getElementsAnnotatedWith(UrlClass.class)) {TypeElement typeElement = (TypeElement) element;ListelementList = typeElement.getEnclosedElements();HashMap urls =newHashMap<>();for(Elemente : elementList) {Urlurl = e.getAnnotation(Url.class);if(url !=null) {if(!url.value().startsWith("/") && !url.value().startsWith("http")) {thrownewRuntimeException("url should start with / or http");}VariableElement variableElement = (VariableElement) e;urls.put(variableElement.getConstantValue().toString(), url.value());}}MethodSpec constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).build();StringBuilder sb =newStringBuilder();Iteratoriter = urls.entrySet().iterator();while(iter.hasNext()) {Map.Entry entry = (Map.Entry) iter.next();Objectkey = entry.getKey();Objectval = entry.getValue();sb.append("urls.put(\"").append(key).append("\"").append(",\"").append(val).append("\");");}intinitialCapacity = urls.size();MethodSpec init = MethodSpec.methodBuilder("init").addModifiers(Modifier.PUBLIC).addAnnotation(Override.class).returns(HashMap.class).addCode("HashMap urls = new HashMap("+ initialCapacity +");"+ sb.toString()).addCode("return urls;").build();StringclassNameStr = typeElement.getSimpleName().toString();ClassName className = ClassName.get(PACKAGE,CLASSNAME_SUFFIX);TypeSpec settingManager = TypeSpec.classBuilder(classNameStr +CLASSNAME_SUFFIX).addModifiers(Modifier.PUBLIC, Modifier.FINAL).addSuperinterface(className).addMethod(constructor).addMethod(init).build();Stringfull = typeElement.getQualifiedName().toString();StringpackageName = full.substring(0, full.indexOf(classNameStr)-1);JavaFile javaFile = JavaFile.builder(packageName, settingManager).build();try{javaFile.writeTo(processingEnv.getFiler());}catch(IOException e) {e.printStackTrace();}}returntrue;}@OverridepublicSourceVersion getSupportedSourceVersion() {returnSourceVersion.RELEASE_7;}@Overridepublic synchronizedvoidinit(ProcessingEnvironment processingEnv) {super.init(processingEnv);}}

其中的process方法就是处理Annotation的核心方法,可以获取到Annotation,与反射的思路类似。

4、运行

运行之后会自动生成Processor处理后的java类

怎么调试?

如何调试process方法?这和一般App调试是不一样的,因为process方法是运行在编译期,所以需要配置远程调试。

新建一个调试

其他保持默认即可

配置要调试的Task

复制进去刚才remote的command参数,注意suspend改成y

在process方法里加断点

点击运行

点击启动调试器,过会儿就会在断点停住了

参考资料:

1、http://www.jianshu.com/p/2494825183c5

2、http://blog.csdn.net/tomatomas/article/details/53998585