LucasDevLucasDev
文章关于
© 2026 LucasDev. 保留所有权利。
苏ICP备20047064号苏公网安备32011402011764号

AndroidAnnotations 源码阅读(2)— 工作流程

2015/12/10
Android
2.2k 字
Android源码阅读
上一篇文章我们大致了解了 AndroidAnnotations 的基本原理,这篇文章将详细描述该框架的工作流程。

工作流程

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

AndroidAnnotationProcessor 重写了 getSupportedAnnotationTypes 方法。

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

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

AndroidAnnotationProcessor 的类图如下:

annotationprocessor.png

图片加载失败

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 文件中的版本号。

java
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");
  }
}
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

图片加载失败

manifestfinder.png

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

manifest.png

图片加载失败

manifest.png

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

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

由 rclass 包中的 AndroidRClassFinder 和 ProjectRClassFinder 完成

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

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

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

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

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

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

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

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

rclass.png

图片加载失败

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

图片加载失败

modelextractor.png

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

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

parentannotation.png

图片加载失败

parentannotation.png

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

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

elementsholder.png

图片加载失败

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

图片加载失败

modelvalidator.png

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

eactivityhandler.png

图片加载失败

eactivityhandler.png

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

annotationhandlers.png

图片加载失败

annotationhandlers.png

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

validatorhelper.png

图片加载失败

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

图片加载失败

modelprocessor.png

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

handler.png

图片加载失败

handler.png

holder.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

图片加载失败

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

图片加载失败

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

图片加载失败

coderesult.png

modelgenerator.png

图片加载失败

modelgenerator.png

相关文章

  • RxJava 源码阅读(2)— 线程调度2016/04/08
  • RxJava 源码阅读(1)— 关键类2016/04/01
  • AndroidAnnotations 源码阅读(1)— 基本原理2015/12/01

目录