玩转Android 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方法里加断点
点击运行
点击启动调试器,过会儿就会在断点停住了
参考资料: