android-apt 與 AnnotationProcessor
APT(Annotation Processing Tool)是一種處理注釋的工具,它對(duì)源代碼文件進(jìn)行檢測(cè)找出其中的Annotation,使用Annotation進(jìn)行額外的處理。(額外的處理包括,修改源文件,增加新代碼,甚至添加新的Annotation)
android-apt是一個(gè)gradle插件,始于三年前 ByHugo*Visser*
AnnotationProcessor是Android Gradle 插件 2.2 版本提供的插件
能干什么?
少些代碼(增加開發(fā)效率,代碼看起來更優(yōu)雅)
解耦(ActivityRouter)
誰都在用?
EventBus、Retrofit、Dagger2、ButterKnife等等
像官方靠齊,本文均采用AnnotationProcessor進(jìn)行示范
初體驗(yàn):
AnnotationProcessor 所需環(huán)境:
gradle 插件2.2 +
AndroidStudio 2.2 (AndroidStudio 2.3調(diào)試不成功,未知原因)
所需知識(shí):
Java注解知識(shí)(https://joyrun.github.io/2016/07/18/java-annotation)JavaPoet(http://www.itdecent.cn/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"
當(dāng)然你也可以不新建一個(gè)Module來做,寫在業(yè)務(wù)Module里,但是Compiler要依賴,所以不建議寫在也業(yè)務(wù)里。
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自動(dòng)注冊(cè)進(jìn)去
javapoet:優(yōu)雅的生成Java類的工具
compile project(':annotation') 依賴剛才新建的annotation module 因?yàn)樾枰胊nnotation
注解處理是整個(gè)Annotation Processor的核心,對(duì)Annotation進(jìn)行處理的就是這個(gè)部分。
繼承Java提供的AbstractProcessor,重寫相關(guān)方法就可以實(shí)現(xiàn)對(duì)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、運(yùn)行

運(yùn)行之后會(huì)自動(dòng)生成Processor處理后的java類
怎么調(diào)試?
如何調(diào)試process方法?這和一般App調(diào)試是不一樣的,因?yàn)閜rocess方法是運(yùn)行在編譯期,所以需要配置遠(yuǎn)程調(diào)試。

新建一個(gè)調(diào)試

其他保持默認(rèn)即可

配置要調(diào)試的Task

復(fù)制進(jìn)去剛才remote的command參數(shù),注意suspend改成y

在process方法里加斷點(diǎn)

點(diǎn)擊運(yùn)行

點(diǎn)擊啟動(dòng)調(diào)試器,過會(huì)兒就會(huì)在斷點(diǎn)停住了
參考資料: