Android APT工作原理(annotationProcessor)

前言

在之前的文章Android编译时注解--入门篇(AbstractProcessor、APT)中介绍了通过APT技术实现仿ButterKnife编译时注解的功能。虽然尽可能做到了很详细的介绍(那篇文章长达2500+字),但是仍然留下了一些疑问,本文就是专门来解释这些问题的。

目前市面上大多数讲apt的文章,大多数是讲一个编译时注解的demo,但是很少人把apt工作原理说清楚,对于下面这几个问题更是几乎没人解释过原因。如果你不想只学一点用法,不想面试被卡住,想让自己变的更强,你应该把apt的工作原理搞清楚,本文值得你阅读

一些疑问

  • 注解处理器processor为什么要在META-INF注册?
  • 注解处理器processor是如何被系统调用的?
  • 注解申明和注解处理器为什么要分Module处理?
  • apt项目不会增加apk体积?

先来回顾一下之前项目的部分目录结构

Android APT工作原理(annotationProcessor)_第1张图片
image.png

先明确一些概念:
AbstractProcessor是抽象处理器,开发apt时都必须继承这个类来生成.java文件,实现的这个类后叫做注解处理器,也就是这里的ButterKnifeProcessor

Q1:注解处理器processor为什么要在META-INF注册?


META-INF的作用
META-INF, 相当于一个信息包,用于存放一些meta information相关的文件。用来配置应用程序、扩展程序、类加载器和服务manifest.mf文件,在用jar打包时自动生成。

在之前的文章中说过,通过@AutoService(Processor.class)注解把注解处理器ButterKnifeProcessor注册到META-INF/services中,这里的包名是META-INF/services/javax.annotation.processing.Processor,
这个文件的内容是

com.zx.processor.ButterKnifeProcessor

这个包名其实就是@AutoService(Processor.class)里面的Processor类;而文件内容就是注解处理器。

在编译时,java编译器(javac)会去META-INF中查找实现了的AbstractProcessor的子类,并且调用该类的process函数,最终生成.java文件。其实就像activity需要注册一样,就是要到META-INF注册 ,javac才知道要给你调用哪个类来处理注解。

Q2:注解处理器processor是如何被系统调用的?


一些细心的同学应该发现了这个问题,我们并没有手动调用AbstractProcessor这个注解处理器类,那系统是什么时间调用的?又是如何调用的?这其实就牵扯到apt工作机制。

在上一问中,我们已经了解到,在编译时javac会查找所有的 在META_INF 中注册的注解处理器来处理注解。

到这里,好像有点清楚了,大概知道javac会去找到Processor并调用。但是呢还是没找到直接源头,因为它不像我们面向对象编程中可以准确追踪到是哪个对象调用的。

别着急,先来看这么个东西.
是我项目中使用到注解的app.Gradle

dependencies {
    implementation project(':annotation')
    implementation project(':inject_api')
    //gradle3.0以上apt的实现方式
    annotationProcessor project(':processor')
}

这里的annotationProcessor有点特别,没错,它是APT实现方案的一种。这里简单介绍一下:

APT实现方案
android-aptannotationProcessor功能是一样的,都是apt的实现方案,前者是个人开发者提供,比较早(现在不再维护了),后者是google官方开发的内置在gradle里的apt。

annotationProcessor是APT工具中的一种,是google开发的内置框架,不需要引入,可以直接在build.gradle文件中使用。

只有在你使用注解的地方引入了annotationProcessor,系统才会主动调用注解处理类Processor,才会最终生成如下的.java文件

Android APT工作原理(annotationProcessor)_第2张图片
apt生成类.png

这里先简单总结一下:
2.1、在完成注解处理类Processor之后,需要做2件事情:

  • 1、在META-INF目录下注册Processor
  • 2、在项目中使用注解的地方添加apt工具annotationProcessor

2.2、APT 4要素
  注解处理器(AbstractProcess)+ 代码处理(javaPoet)+ 处理器注册(AutoService)+ apt(annotationProcessor)

APT(Annotation Processing Tool)总结
首先,APT是javac提供的一种工具,它在编译时扫描、解析、处理注解。它会对源代码文件进行检测,找出用户自定义的注解,根据注解、注解处理器和相应的apt工具自动生成代码。这段代码是根据用户编写的注解处理逻辑去生成的。最终将生成的新的源文件与原来的源文件共同编译(注意:APT并不能对源文件进行修改操作,只能生成新的文件,例如往原来的类中添加方法)。具体流程图如下图所示:

Android APT工作原理(annotationProcessor)_第3张图片
apt工作流程.png

APT技术的使用,需要我们遵守一定的规则。大家先看一下整个APT项目项目构建的一个规则图,具体如下所示:


Android APT工作原理(annotationProcessor)_第4张图片

Q3:注解申明和注解处理器为什么要分Module处理?


先来回顾一下之前的项目结构:


Android APT工作原理(annotationProcessor)_第5张图片
  • annotation:申明注解 (java lib)
  • processor:注解处理器(java lib)
  • inject_api:调用处理器中生成的类 (android lib)
  • app:项目使用 (android lib)

我们都知道注解处理器都需要继承AbstractProcessor类,但是AbstractProcessor是JDK中的类,不在android sdk中,所以需要放在单独的java lib中;而processor中需要依赖自定义注解,把annotation抽成一个独立的lib,便于维护。

那注解声明和注解处理为什么要分开呢?可不可以放在一起?
先说结论:可以放在一起,放在一起对功能上没有什么影响;但是一般不放在一起,原因如下:

我们都知道processor的作用是:在编译器解析注解、生成新的.java文件。这个lib只在编译器用到,是不会被打包进apk的。对于调用者来说,你只是想使用这个注解,而不希望你已经编译好的项目中引进注解处理器相关的内容,所以为了不引入没必要的文件,我们一般选择将注解声明和注解处理分开处理。

到这里apt相关知识就说完了,我们也可以理解为什么ButterKnife这种注解库不会增加项目体积了。

想了解更多apt知识,可以参考:
https://www.jianshu.com/p/b6b3283968e0

感谢
你必须知道的APT、annotationProcessor、android-apt、Provided、自定义注解

呕心沥血之作,喜欢的朋友点个赞,刷个666,就是对我的最大支持!

你可能感兴趣的:(Android APT工作原理(annotationProcessor))