美文网首页
编译时注解注意事项

编译时注解注意事项

作者: 奋飞的蜗牛ing | 来源:发表于2018-03-13 23:43 被阅读146次

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是否是一个类:

Element接口

Element的子类

类图 Element的子类

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

TypeElement

VariableElement

VariableElement

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包括基本类型,声明类型(类类型和接口类型),数组,类型变量和空类型。也代表通配类型参数,可执行文件的签名和返回类型等。

疑惑

解释:编译器使用注解处理器来处理目标代码时候,目标源码还没有编译成字节码,所以任何指向目标源码字节码的代码都会发生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。

相关文章

  • 编译时注解注意事项

    1、将@AutoService(Processor.class)误写为@AutoService(Process.c...

  • 编译时注解器初探(一)

    编译时注解器初探(一) 注解处理器 (Annotation Processor) 编译时注解和运行时注解定义的方式...

  • 编译时注解

    今天在写一个框架时用到编译时注解,在传maven的时候老是传不上去,最后发现点击upload时会执行所有的上传ta...

  • java注解

    一、注解分类 源码注解(SOURCE):注解只在源码中存在,编译成.class文件就不存在 编译时注解(CLASS...

  • Android编译时注解初级之ButterKnife

    本文的主要目的在于了解编译时注解,并能初步运用。代码在最后。 1.编译时注解 VS 运行时注解 1.1 运行时注解...

  • 参阅ButterKnife源码,对编译时注解(Annotatio

    编译时注解 运行时注解和编译时注解,两种注解方式对性能的影响是不一样的。之前看到相关资料,都说对于注解的优化,都用...

  • java常用注解分类

    1)按照运行机制划分: 【源码注解→编译时注解→运行时注解】 源码注解:只在源码中存在,编译成.class文件就不...

  • ButterKnife源码解析

    参考1参考2参考3参考4 一:基本原理 编译时注解+APT编译时注解:Rentention为CLASS的直接。保留...

  • Android APT注解处理器

    一、编译时技术简介APT ( Annotation Processing Tool ) 注解处理工具 ; 编译时技...

  • 编译时注解处理方

    编译时注解处理 若希望对编译时的注解进行处理需要做 自定义类集成自AbstractProcessor 重写其中的p...

网友评论

      本文标题:编译时注解注意事项

      本文链接:https://www.haomeiwen.com/subject/sjcxqftx.html