AndroidAnnotations 源码阅读(2)— 工作流程
发布于:2015-12-10 18:55 最后更新于:2015-12-10 18:55

摘要: 上一篇文章我们大致了解了 AndroidAnnotations 的基本原理,这篇文章将详细描述该框架的工作流程。

工作流程

首先是 AndroidAnnotationProcessor 类,该类继承自 AbstractProcessor ,即之前提到的注解处理器。它是注解处理程序的入口,控制整个处理流程。

AndroidAnnotationProcessor 重写了 getSupportedAnnotationTypes 方法。

@Override
public Set<String> getSupportedAnnotationTypes() {
oreturn annotationHandlers.getSupportedAnnotationTypes();
}

annotationHandlers 类存储了 AndroidAnnotations 所支持的一百多个注解的名称及其对应的 Handler。

AndroidAnnotationProcessor 的类图如下: annotationprocessor.png

根据上一篇文章中的日志输出和上面的类图,我们可以知道 AndroidAnnotations 工作流程分为以下七个步骤:

  1. 检查 core 和 api 版本号是否一致(checkApiAndCoreVersion)
  2. 查找并解析 Manifest 文件(extractAndroidManifest)
  3. 查找并合并 android.R.class 和项目的 R.class (findRClasses)
  4. 提取注解(extractAnnotations)
  5. 验证注解(validateAnnotations)
  6. 处理注解(processAnnotations)
  7. 生成代码文件(generateSources)

1. 检查 core 和 api 版本号是否一致

由 AndroidAnnotationProcessor 自己完成

androidannotations.jar 中包含 androidannotations.properties 文件,androidannotations-api.jar 中包含 androidannotations-api.properties 文件, 比较两个 properties 文件中的版本号。

private void checkApiAndCoreVersions() throws VersionMismatchException {
  String apiVersion = getAAApiVersion();
  String coreVersion = getAAProcessorVersion();

  if (!apiVersion.equals(coreVersion)) {
    LOGGER.error("AndroidAnnotations version for API ({}) and core ({}) doesn't match. Please check your classpath", apiVersion, coreVersion);
    throw new VersionMismatchException("AndroidAnnotations version for API (" + apiVersion + ") and core (" + coreVersion + ") doesn't match. Please check your classpath");
  }
}

2. 查找并解析 Manifest 文件

由 helper 包中的 AndroidManifestFinder 完成

AndroidMainfestFinder 查找 Manifest 文件并解析出其中的组件、权限等,将解析结果存放在 (helper.)AndroidManifest 中。

该步骤可以分为 查找 和 解析 两个部分:

  1. 查找 Manifest 文件
    如果在 APT 参数中指定了 AndroidManifest.xml 的路径,那么 Finder 直接到指定路径中查找;如果没有指定,那么 Finder 将从项目的根目录开始查找,如果没有找到,则向上一级目录中查找。
  2. 解析 AndroidManifest.xml 文件
    这部分就是将 xml 中的各个组件、权限等提取出来,存放到 AndroidManifest 对象中。

manifestfinder.png

AndroidManifestFinder 中的 manifestNameToValidQualifiedName 方法用于将 AndroidManifest.xml 中的简写的 .MainActivity_ 转化成 com.annotation.example.MainActivity_ 全限定名,这样就可以根据组件名称找到对应的组件类。

manifest.png

AndroidManifest 类用于存储解析得到的组件、权限等。

3. 查找并合并 android.R.class 和项目的 R.class

由 rclass 包中的 AndroidRClassFinder 和 ProjectRClassFinder 完成

AndroidRClassFinder 和 ProjectRClassFinder 分别查找 android.R.class 和项目的 R.class ,将两个 Rclass 的内容合并到 (rclass.)CoumpoundRClass 中。

这两个 Finder 比较简单,以 AndroidRClassFinder 为例:

Elements elementUtils = processingEnv.getElementUtils();
TypeElement androidRType = elementUtils.getTypeElement("android.R");

ProcessingEnvironment processingEnv 是由 APT 传到注解处理器,即该项目的 AndroidAnnotationProcessor 中的。我们通过 processingEnv 可获得 elementUtils ,调用它的 getTypeElement 就可以获得全限定名对应的类型元素(类、接口等),在这里 TypeElement 就对应 android.R.class。这个 TypeElement 会被传递给 RClass 作为构造参数。

List<? extends Element> rEnclosedElements=rClassElement.getEnclosedElements();
ElementFilter.typesIn(rEnclosedElements);

typesIn 返回类型为 List,在这里对应 android.R.class 中的各个内部类。然后将代表内部类的 TypeElement 传递给 RInnerClass 作为构造参数。

List<VariableElement> idFields = ElementFilter.fieldsIn(idEnclosedElements);

VariableElement 代表内部类中的各个字段,我们可以从 VariableElement 获得各个字段的名称和对应的值。

rclass.png

  • IRClass 用于表示 R.class 文件
  • IRInnerClass 用于表示 R.class 中的内部类,如 attr, id 等
  • CoumpoundInnerClass 将 android.R.class 和项目的 R.class 文件中同名内部类的资源整合起来
  • CoumpoundRClass 则包含了多个 CoumpoundInnerClass,整合了 android.R.class 和 R.class 中的所有资源。

Elements、TypeElement、ElementFilter 这些类都是 javax.lang.model 包中的。javax.lang.model 用来为 Java 编程语言建立模型,此包及其子包的成员适用于语言建模、语言处理任务和 API (包括但并不仅限于注解处理框架)。与 JCodeModel 类似,也是使用 Java 来描述 Java 代码,但是 JCodeModel 更多的是被看作 Java 代码生成器。

由于从注解处理工具中得到的代码元素是使用 javax.lang.model.* ; 来表示的,而我们将要生成的代码是用 com.sun.codemodel.* ; 来表示的,比如一个方法在前者中使用 ExecutableElement 表示,而在后者中使用 JMethod 表示,所以在后面的步骤中会涉及到两种表示方式之间的转换。

4. 提取注解

由 model 包中的 ModelExtractor 完成

ModelExtractor 提取要编译的代码中的注解,提取出的注解存放在 (model.)AnnotationElementsHolder 中。

ModelExtractor 提取注解分为两部分:

  1. 提取一个类的所有祖先类的注解(extractAncestorsAnnotations)
  2. 提取一个类本身包含的注解(extractRootElementAnnotations)

modelextractor.png

为什么要提取祖先类的注解?
我们将之前的 SecondActivity 改为:

@EActivity(R.layout.activity_main)
public class SecondActivity extends MainActivity {
}

parentannotation.png

MainActivity 中的 textView 使用了 @ViewById 注解,在生成的 MainActivity_ 中生成 findViewById(id.textView) 语句进行初始化。但是 SecondActivity 类中并没有显式地写出使用 @ViewById 注解的 textView,只处理 SecondActivity 类中的注解是无法生成 findViewById(id.textView) 这句代码的。因此我们需要将 MainActivity 中的 @ViewById 注解在 SecondActivity 中再处理一遍,这样才能在 SecondActivity_ 中对 textView 进行初始化。

该阶段提取出的注解被存放在 AnnotationElementsHolder 中。

elementsholder.png

rootAnnotatedElementByAnnotation 存储的是类中的注解,String 是注解的名称,Set 是被该注解标注的元素的集合。 ancestorAnnotatedElementsByAnnotation 存储的是类的祖先类中的注解,String 是注解的名称,Set 是(类的祖先类中被该注解标注的元素,类)的集合。

两个用于提取注解的方法描述如下:

  1. extractAncestorsAnnotations
    向上查找某个类的祖先类,直到 Object 类为止
    将祖先类中的注解、被注解的元素以及这个类存放到ancestorAnnotatedElementsByAnnotation中。
  2. extractRootElementAnnotations
    将某个类中的注解、被注解的元素存放到 rootAnnotatedElementByAnnotation 中。

5. 验证注解

由 process 包中的 ModelValidator 完成

ModelValidator 获取 (model.)ModelExtractor 提取出的注解(存放在 AnnotationElementsHolder 中),交给 (handler.)AnnotationHandlers 检查,AnnotationHandlers 会调用各个 handler 的 validate 方法进行验证。验证后的注解存放在 (model.)AnnotationElementsHolder 中。

modelvalidator.png

每个注解对应一个 Handler,如 @EActivity 对应 EActivityHandler。 每个 Handler 包含 validate 和 process 两个方法,分别用于验证和处理注解。

eactivityhandler.png

handler 包的 AnnotationHandlers 类存储了所有的 handler。

annotationhandlers.png

每个 Handler 的 validate 方法会使用 ValidatorHelper 帮助验证,如判断方法是否为 private、final,判断注解所在的类是否有 EActivity 注解 (enclosingElementHasEActivity) 等。

validatorhelper.png

ValidatorHelper 中会使用 AnnotationHelper 类,这个类会通过 Logger 输出警告和错误信息。在 Logger 部分,AndroidAnnotations 通过传递给注解处理器的 ProcessingEnvironment 获得 javax.annotation.processing.Messager。使用 Messager 可以向 APT 报告警告和错误信息,这样就可以在 IDE 中的错误代码处直接显示警告和错误信息。

6. 处理注解

由 process 包中的 ModelProcessor 完成

ModelProcessor 获取 (process.)ModelValiator 验证之后的注解,对于将要生成类的注解(例如 EActivity ),ModelProcessor 先生成对应的 (holder.)GeneratedClassHolder 用来存储所要生成的代码结构,然后由 (handler.)AnnotationHandlers 将处理任务分配到各个 handler,由 handler 的 process 方法完成对应的 GeneratedClassHolder 的代码。处理完成后,要生成的代码存放在 (process.)ModelProcessor.ProcessResult 中。

modelprocessor.png

为了更好地理解注解是如何被处理的,我们需要先了解 Annotation 、Handler 和 Holder 的分类以及对应关系。

handler.png holder.png

Annotation
在 AndroidAnnotations 中,注解被分为两大类,一类是将会生成类(Generating)的注解,如 @EActivity 、@EProvider 等,另一类是用于在前者生成的类中补充代码(Decorating)的注解,如 @ViewById 、@AfterView 等。

Handler
对应于两类注解,handler也被分成两类:GeneratingAnnotationHandler 和 DecoratingAnnotationHandler。 GeneratingAnnotationHandler 继承自 BaseGeneratingAnnotationHandler,用于处理那些将会生成类的注解,如 EActivityHandler 、EProviderHandler 等。其他的 handler 则属于 DecoratingAnnotationHandler,用于装饰,在 GeneratingAnnotationHandler 对应的类中添加代码片段,如 ViewByIdHandler。

Holder
GeneratingAnnotationHandler 通常对应一个 GeneratedClassHolder,用来表示要生成的类。如 EActivityHandler 对应于 EActivityHolder。 BaseGeneratingAnnotationHandler 在 GeneratedClassHolder 中补充代码。一些 DecoratingHandler 使用 Decorator(也是一种 Holder)存储一部分代码片段,如 OnActivityResultHolder。

annotationhandlers.png

handler 包的 AnnotationHandlers 类存储了所有的 handler。AnnotationHandlers 在将所有 Handler 放入 annotationHandlers 的同时,也按照其类型分别放到 generatingAnnotationHandlers 和 decoratingAnnotationHandlers 中。

现在我们可以知道注解处理分为两步:

  1. 生成类
    从 AnnotationHandlers 中取出 generatingAnnotationHandlers,先对 Generating 类型的注解进行处理,生成对应的 ClassHolder 用来存储生成的类。如果处理的类是抽象类,则跳过处理。如果处理的类是内部类,并且其外部类还没有生成对应的 ClassHolder,则先处理外部类上的 Generating 注解,等外部类的 ClassHolder 生成之后再生成内部类的 ClassHolder。

  2. 补充代码
    从 AnnotationHandlers 中取出 decoratingAnnotationHandlers,在处理 Decorating 这类注解时,先处理标注在祖先类中元素上的注解,然后再处理直接标注在类中的注解。

codemodelhelper.png

每个 Handler 的 process 方法会使用 APTCodeModelHelper 处理代码模型,如查找方法 (findAlreadyGeneratedMethod) 、重写方法 (overrideAnnotatedMethod) 、给方法添加参数 (addParamToMethod) 等。

从 APT 中得到的代码元素是使用 javax.lang.model.* ; 来表示的,而我们将要生成的代码是用 com.sun.codemodel.* ; 来表示的,所以 APTCodeModelHelper 会涉及到两种表示方式之间的转换,如 typeMirrorToJClass。DeclaredType 和JClass 分别在 javax.lang.model.* 和 com.sun.codemodel.* 中表示类或接口。

7. 生成代码

由 generation 包中的 CodeModelGenerator 完成

CodeModelGenerator 获取 (process.)ModelProcessor 的处理结果 ((process.)ModelProcessor.ProcessResult),由结果生成代码文件。

coderesult.png

modelgenerator.png

评论 登录后评论

没有评论