1、将@AutoService(Processor.class)误写为@AutoService(Process.class)。并在相应模块gradle中compile'com.google.auto.service:auto-service:1.0-rc2'
@AutoService作用:生成META-INF/service/javax.annotation.processing.Processor
2、分多个module
ioc-annotation 用于存放注解等,Java模块(java-library)
ioc-compiler 用于编写注解处理器,Java模块(java-library)
ioc-api 用于给用户提供使用的API,为Andriod模块(library)
ioc-sample 示例,为Andriod模块(application)
注意依赖关系:ioc-compiler依赖ioc-annotation;ioc-sample依赖ioc-api;ioc-api依赖ioc-annotation。请ioc-sample依赖annotationProcessor project (':ioc-compiler')
3、在gradle中用annotationProcessor代替apt
annotationProcessor和apt的作用:注解处理类不应该被打包到APK中来增加apk的大小,它只有在编译时被用到。
4、生成的类放在app/build/generate/source/apt/
*5、注解编译时的调试见:http://blog.csdn.net/tomatomas/article/details/53998585(该操作流程修改为:启动远程JVM运行compileDebugJavaWithJavac,然后应该先启动调试器AnnotationProcessor,连接成功后,再启动远程JVM运行compileDebugJavaWithJavac。suspend=y/n 是否在调试客户端建立连接之后启动 VM ,必须为y。 )【搞了一天,心得:若不能成功,最好重启AS,重新来过;成功之后再次调试,processor类必须有修改,或clean project】
由于AndroidStudio中Gradle脚本是运行在Remote进程上的,所以无法直接调试。注解处理器是运行它自己的虚拟机JVM中。是的,你没有看错,javac启动一个完整Java虚拟机来运行注解处理器。这对你意味着什么?你可以使用任何你在其他java应用中使用的的东西。使用guava。如果你愿意,你可以使用依赖注入工具,例如dagger或者其他你想要的类库。但是不要忘记,即使是一个很小的处理,你也要像其他Java应用一样,注意算法效率,以及设计模式。
但是在终端输入:(调试成功,但这种方式没有上面的使用方便)
./gradlew assembleDebug -Dorg.gradle.daemon=false -Dorg.gradle.debug=true
编译时注解具体介绍:http://blog.csdn.net/lmj623565791/article/details/51931859
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1、
getName()返回的是虚拟机里面的class的表示
getCanonicalName()返回的是更容易理解的表示
对于普通类来说,二者没什么区别,但对于array或内部类等就有所不同。
Name.class.getCanonicalName(): testName.Name
Name.class.getName(): testName.Name
Name.class.getSimpleName(): Name
Name.Inner.class.getCanonicalName(): testName.Name.Inner
Name.Inner.class.getName(): testName.Name$Inner
Name.Inner.class.getSimpleName(): Inner
args.getClass().getCanonicalName(): java.lang.String[]
args.getClass().getName(): [Ljava.lang.String;
args.getClass().getSimpleName(): String[]
getSimpleName()只是类名。
在load class的时候需要的名字也是getName()这种的名字,在根据类名字创建文件的时候最好使用getCanonicalName()
2、
Set<?extends Element> set = roundEnv.getElementsAnnotatedWith(BindView.class);
Element接口中的方法
在注解处理过程中,我们扫描所有的Java源文件。源代码的每一个部分都是一个特定类型的Element。换句话说:Element代表程序的元素,例如包、类或者方法。roundEnv.getElementsAnnotatedWith(Factory.class))返回所有被注解了@Factory的元素的列表。你可能已经注意到,我们并没有说“所有被注解了@Factory的类的列表”,因为它真的是返回Element的列表。请记住:Element可以是类、方法、变量等。所以,接下来,我们必须检查这些Element是否是一个类:
![](https://img.haomeiwen.com/i6116263/b56ddb5d0f5b6a2e.png)
Element的子类
![](https://img.haomeiwen.com/i6116263/5a13f1e84a48d6d6.png)
![](https://img.haomeiwen.com/i6116263/dac50dd4f3fb79d0.png)
package com.example; // PackageElement
public class Foo { // TypeElement
private int a; // VariableElement
private Foo other; // VariableElement
public Foo () {} // ExecuteableElement
public void setA ( // ExecuteableElement
int aaa // TypeParameterElement
) {}
}
核心的两个子分别是TypeElement和VariableElement。
TypeElement
![](https://img.haomeiwen.com/i6116263/f0218a419b5431db.png)
VariableElement
![](https://img.haomeiwen.com/i6116263/1fbd4f91769b73b8.png)
3、
BindView bindAnnotation = variableElement.getAnnotation(BindView.class);
int id = bindAnnotation.value();
PackageElement packageElement = elementUtils.getPackageOf(typeElement) //生成的类和原类同包。
protected ProcessingEnvironment processingEnv; //是AbstractProcessor类中的,可被子类使用。
JavaFileObject jfo =processingEnv.getFiler().createSourceFile(proxyInfo.getProxyClassFullName(), proxyInfo.getTypeElement());
element.asType().toString(); //返回对应的数据类型
process 函数返回值表示这组 annotations 是否被这个 Processor 接受,如果接受后续的 processor 不会再对这个 Annotations 进行处理。
Messager提供给注解处理器一个报告错误、警告以及提示信息的途径。它不是注解处理器开发者的日志工具,而是用来写一些信息给使用此注解器的第三方开发者的。
4、
TypeMirror
这三个类也需要我们重点掌握:
DeclaredType代表声明类型:类类型还是接口类型,当然也包括参数化类型,比如Set,也包括原始类型
TypeElement代表类或接口元素,而DeclaredType代表类类型或接口类型。
TypeMirror代表java语言中的类型.Types包括基本类型,声明类型(类类型和接口类型),数组,类型变量和空类型。也代表通配类型参数,可执行文件的签名和返回类型等。
![](https://img.haomeiwen.com/i6116263/e5629565712b069c.png)
解释:编译器使用注解处理器来处理目标代码时候,目标源码还没有编译成字节码,所以任何指向目标源码字节码的代码都会发生MirroredTypeException【Class<?> clazz=annotation.value();会抛出异常】。主要是针对于:注解中的value值是【@Factory(id ="Margherita", type = Meal.class)】
TypeMirror mirror=element.asType()
Types types=processingEnvironment.getTypeUtils()
Element element=types.asElement(typeMirror)
TypeMirror是用来反应类本身信息的类,在继承树移动必须用到TypeMirror,比如查找某个类的父类
Types typeUtils = processingEnv.getTypeUtils();
while(!element.toString().equals(Object.class.getName())) {
TypeMirror typeMirror=element.getSuperclass();
element = (TypeElement)typeUtils.asElement(typeMirror);
}
5、
注解处理过程可能会多于一次。官方javadoc定义处理过程如下:注解处理过程是一个有序的循环过程。在每次循环中,一个处理器可能被要求去处理那些在上一次循环中产生的源文件和类文件中的注解。原因是这样的,这些生成的文件中也可能包含@Factory注解,它们还将会被FactoryProcessor处理。产生的问题:在第二轮的process()中,任然保存着第一轮的数据,并且会尝试生成在第一轮中已经生成的文件,从而导致这个错误的出现。
Attempt to recreate a file for type com.hannesdorfmann.annotationprocessing101.factory.MealFactory
解决:factoryClasses.clear();
关键的点是:我们要记住注解处理过程是需要经过多轮处理的,并且你不能重载或者重新创建已经生成的源代码。
6、分包机制
将注解和处理器分成两个模块,因为使用者不需要在他已经编译好的项目中包含处理器相关的代码。如果你是一个Android的开发者,你肯定听说过65k个方法的限制(即在一个.dex文件中,只能寻址65000个方法)。
7、
实例化生成的对象,使用反射机制,但是反射机制不是很慢么,我们使用注解处理来生成本地代码,会不会导致很多的反射性能的问题?的确,反射机制的性能确实是一个问题。然而,它不需要手动去创建对象,确实提高了开发者的开发速度。ButterKnife中有一个哈希表HashMap来缓存实例化过的对象。所以MyActivity$ $ViewInjector()只是使用反射机制实例化一次,第二次需要MyActivity$ $ViewInjector()的时候,就直接从哈希表中获得。
8、
javax.annotation.processing.Processor中的书写顺序决定注册处理器的执行顺序
假设该文件中定义如下:
package.ProcessorB
package.ProcessorA
那么编译器每一轮扫描会先执行处理器ProcessorB,再执行处理器ProcessorA。假如ProcessorB中的process()方法返回true,则表示消费完这轮的注解扫描,将不再执行ProcessorA,只有当返回false时,才会接下来执行ProcessorA。
PS:每个注解处理器在整个过程中都保持同一个实例。
9、
简单来说,Element代表源代码,TypeElement代表的是源码中的类型元素,比如类。虽然我们可以从TypeElement中获取类名,TypeElement中不包含类本身的信息,比如它的父类,要想获取这信息需要借助TypeMirror,可以通过Element中的asType()获取元素对应的TypeMirror。
网友评论