Android APT 自动生成代码实践
Android APT 自动生成代码实践
前言:都9102年了,我才接触到apt的东西,之前自己压根儿没往那儿想。一直都能见到作用在onCreate()方法上的@Override注解,一直以为是一种约定俗成的东西。直到遇到EventBus,也注意到了它的注解,仅仅觉得:呀,他也有注解,约定了这么这么用,真方便!
直到最近在努力提升技术栈时,才发现:注解是一门技术,而不仅仅是一个约定。可以提高编码效率的东西必须学习!
最近实践的时候参考了网上很多文章,参考着然后一句句代码敲下来,结果我的项目始终生成不了Java文件。然后忙了半天查找问题:不是androidx的问题,依赖的也都是同样的auto-service,然后把别人项目拉下来跑也没问题,我自己在别人项目上改也没问题,怎么就自己的工程不行呢?终于,在忙活了半天依旧找不到头脑后,我下班回家睡觉了。
第二天醒来,我的脑子里就一个想法:别是gradle升级的问题!!!!!然后就直接搜到了Android Gradle由4.x升级至5.0导致Apt项目失效 这篇文章!想解决很简单 添加 一个annotationProcessor "com.google.auto.service:auto-service:1.0-xxxxx"
就好,注意是 annotationProcessor !
接下来就把整个入门过程记录下来吧,有坑的地方会重点标出。本篇文章不需要你多高的技术,只要你是Android入门级就可以操作。
1、工程准备
新建工程,例如我这里工程名为APT。在工程中创建两个 java 的module,一个annotation用于声明注解,就是存放类似于@Override的这些东西。一个processor用于处理声明的注解。
1.1、创建annotation模块
点击 File -> New Module 在弹出的对话框中选择 Java Library,然后输入模块名annotation即可。然后我们新建注解类MyAnnotation。
如下所示(所有模块都包含了):
然后我们在MyAnnotation中编写如下代码:
package com.test.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.CLASS) //表示编译时的注解
@Target(ElementType.FIELD) //表示作用在变量上
public @interface MyAnnotation {
}
这里有几点需要注意:
- @interface 注解类需要用这个来标识
- @Target(ElementType.FIELD) 这个表示注解作用在变量上,还有其他的类型这里我们都先不管,一切以讲解为主
- @Retention(RetentionPolicy.CLASS)这个表示在编译的时候也就是我们as在build的时候会编译出来代码,还有其他的类型这里我们也先不管,一切以讲解为主
到这里,你的注解已经完成了,你可以在app模块里直接引用该 annotation模块,然后在类里面新建变量看是不是已经可以使用这个@MyAnnotation注解了。使用确实是可以了,但是完全没有用,因为不知道这个注解是干什么用的,所以不会生成代码,下面就来讲解怎么根据这个注解来生成代码,我们就简单的让他生成一个空的类就好了,简单明了,代码很少。
1.2、创建processor模块
同上,注意也是选择 Java Library ,然后在该模块的 build.gradle 文件中添加依赖 【注意:我的gradle是5.1.1版本的,如果无法生成代码请先检测gradle的版本,根据版本解决相应问题。如果gradle版本低于3.0请不要参考该文章】:
implementation project(':annotation')//依赖刚刚创建的annotation模块
implementation "com.google.auto.service:auto-service:1.0-rc4"//自动配置的
annotationProcessor "com.google.auto.service:auto-service:1.0-rc4" //这个在gradle5.0以上需要的
implementation 'com.squareup:javapoet:1.11.1'//方便编写代码的
然后在依赖外面添加以下代码,主要为了解决不能在编码中出现中文注释的问题,当然你把注释全删了,这句代码就可以不要了:
// 解决build 错误:编码GBK的不可映射字符
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
完了之后,点击 Sync Project With Gradle Files,就是那个大象图标,导入依赖完毕后,新建MyAnnotationProcessor类编写如下代码:
package com.test.processor;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import com.test.annotation.MyAnnotation;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
@AutoService(Processor.class)
public class MyAnnotationProcessor extends AbstractProcessor {
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> supportTypes = new LinkedHashSet<>();
supportTypes.add(MyAnnotation.class.getCanonicalName());
return supportTypes;
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 创建Java类【你的类名】
TypeSpec autoClass = TypeSpec.classBuilder("AutoClass").build();
// 创建Java文档【这里定义了你的包名,随便写即可】
JavaFile javaFile = JavaFile.builder("com.apt.demo", autoClass).build();
// 将文档写入
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
}
代码已经尽量精简了,这里有几点需要提一下:
- @AutoService(Processor.class)这个注解类可以帮助我们生成一些相关的配置
- getSupportedAnnotationTypes() 这个方法必须复写,将我们定义的注解类MyAnnotation添加进来
- process() 这个方法也必须复写,该方法主要就是生成java类相关的了,需要用到com.squareup:javapoet提供的包
- 在process() 方法中写的时候定义了java类的 包名 以及 类名 信息,编译完后成可以在下文看到相应的代码
2、见证奇迹的时刻
当processor模块的代码编写完毕后,将 annotation和processor模块都添加进app模块的依赖中,然后点击 Build -> Rebuild Project,等待编译器编译完毕后,你就可以在编译器文件夹中找到编译好后的文件啦,如下图所示:
注意我们在代码中声明的包名,类名,这里就一一对应上了吧。
到此我们的入门已经完成了,然后你就可以去看那些 使用apt来写一个自己的BufferKnife 的文章了,只需要注意下processor包中导入的依赖问题即可,gradle升级可真不省心。
然后还要学习的地方就是squareup的 javapoet ,这个可以非常方便的帮我们组织代码,省去了自己使用StringBuilder拼接字符串来写代码的麻烦了,所以我代码里根本没有写这么复杂的东西,我惧怕复杂 [捂脸],也怕把简单的入门文章写得麻烦。
一起学习吧,用好apt技术可以给我们的开发带来很多便利。看看那些主流框架Dagger2、ButterKnife、ARouter等等,我们还有什么理由不去学习呢,加油。