04注解处理器(APT)是什么?——《Android打怪升级之旅》
Posted 老匡话Android
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了04注解处理器(APT)是什么?——《Android打怪升级之旅》相关的知识,希望对你有一定的参考价值。
感谢大家和我一起,在android世界打怪升级!
上一篇讲完注解,这篇咱们科普一下注解的其中一种用途——注解处理器(APT),文章会手把手的帮助大家学会APT的使用。
一、定义
注解处理器(Annotation Processing Tool,简称APT),是JDK提供的工具,用于在编译阶段未生成class之前对源码中的注解进行扫描和处理。处理方式大部分都是根据注解的信息生成新的Java代码与文件。
APT使用相当广泛,EventBus、ARouter、ButterKnife等流行框架都使用了该技术。
二、生成注解处理器
2.1 创建注解模块
① 在项目中新建Module,选择【Java or Kotlin Library】,名字和包名随意填入,点击Finish。
② 在模块中定义注解,注解保留范围选择SOURCE即可,因为APT是作用在源码阶段的,生成class之前。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Test
2.2 创建注解处理器模块
① 在项目中新建Module,选择【Java or Kotlin Library】,名字和包名随意填入,点击Finish。
② 修改新建Module的build.gradle文件,根据是否使用Kotlin分为两种情况。
项目不使用Kotlin:
apply plugin: "java-library"
dependencies
// 注解模块(必选)
implementation project(':lib-annotation')
// 注解处理器(必选)
compileOnly 'com.google.auto.service:auto-service:1.0-rc7'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'
// 生成代码方式之一(可选)
implementation 'com.squareup:javapoet:1.13.0'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
项目使用Kotlin:
apply plugin: "java-library"
apply plugin: "kotlin"
apply plugin: "kotlin-kapt"
dependencies
// 注解模块(必选)
implementation project(':lib-annotation')
// 注解处理器(必选)
kapt 'com.google.auto.service:auto-service:1.0-rc7'
implementation 'com.google.auto.service:auto-service:1.0-rc7'
// 生成代码方式之一(可选)
implementation 'com.squareup:javapoet:1.13.0'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
2.3 创建注解处理器
在2.2的注解处理器模块中新建类,继承自AbstractProcessor类。
使用@AutoService、@SupportedAnnotationTypes、@SupportedSourceVersion注解注释该类,注解处理器即创建完成,具体如下:
// 让该类拥有了获取注解的能力(必选)
@AutoService(Processor.class)
// 设置该处理器支持哪几种注解(必选)
@SupportedAnnotationTypes(Const.CARD_ANNOTATION,Const.TEST_ANNOTATION)
// 源码版本(可选)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class TestAnnotationProcessor extends AbstractProcessor
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
return false;
2.4 在app模块中引入注解处理器
在主项目app模块的build.gradle中引入注解处理器,使用kapt或annotationProcessor修饰,所以在gradle中看到这两种修饰的项目就是注解处理器项目了。
dependencies
// 注解模块
implementation project(":lib-annotation")
// 注解处理器模块,以下二选一
// 使用Kotlin选择这种
kapt project(":compiler")
// 使用Java选择这种
annotationProcessor project(":compiler")
2.5 测试
① 在项目中使用@Test注解
@Test
public class MainActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
② 在注解处理器的init方法中获取Messager对象,在process方法中获取到注解后输出日志查看被注解的类名。
@AutoService(Processor.class)
@SupportedAnnotationTypes(Const.TEST_ANNOTATION)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class TestAnnotationProcessor extends AbstractProcessor
private Messager messager;
@Override
public synchronized void init(ProcessingEnvironment processingEnv)
super.init(processingEnv);
// 获取Messager对象
messager = processingEnv.getMessager();
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
// 获取所有的被Test注解的对象,无论是类还是属性都会被封装成Element
for (Element element : roundEnv.getElementsAnnotatedWith(Test.class))
messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>>>>>>>>>>GetAnnotation:" + element.getSimpleName());
return false;
③ 构建项目,查看日志,成功获取到注解注释过的类名
三、生成代码
获取到了注解信息,下一步就是根据注解生成Java代码了。目前生成Java代码有两种方式,原始方式和JavaPoet。
3.1 原始方式
原始方式就是通过流一行一行的手写代码,这种方式的优点是可读性高,缺点是扩展性差。
咱们看一下EventBus的源码就能更深刻的理解什么是原始方式:
// 截取EventBusAnnotationProcessor.java中的片段
private void createInfoIndexFile(String index)
BufferedWriter writer = null;
try
JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(index);
int period = index.lastIndexOf('.');
String myPackage = period > 0 ? index.substring(0, period) : null;
String clazz = index.substring(period + 1);
writer = new BufferedWriter(sourceFile.openWriter());
if (myPackage != null)
writer.write("package " + myPackage + ";\\n\\n");
writer.write("import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;\\n");
writer.write("import org.greenrobot.eventbus.meta.SubscriberMethodInfo;\\n");
writer.write("import org.greenrobot.eventbus.meta.SubscriberInfo;\\n");
writer.write("import org.greenrobot.eventbus.meta.SubscriberInfoIndex;\\n\\n");
writer.write("import org.greenrobot.eventbus.ThreadMode;\\n\\n");
writer.write("import java.util.HashMap;\\n");
writer.write("import java.util.Map;\\n\\n");
writer.write("/** This class is generated by EventBus, do not edit. */\\n");
writer.write("public class " + clazz + " implements SubscriberInfoIndex \\n");
writer.write(" private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;\\n\\n");
writer.write(" static \\n");
writer.write(" SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();\\n\\n");
writeIndexLines(writer, myPackage);
writer.write(" \\n\\n");
writer.write(" private static void putIndex(SubscriberInfo info) \\n");
writer.write(" SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);\\n");
writer.write(" \\n\\n");
writer.write(" @Override\\n");
writer.write(" public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) \\n");
writer.write(" SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);\\n");
writer.write(" if (info != null) \\n");
writer.write(" return info;\\n");
writer.write(" else \\n");
writer.write(" return null;\\n");
writer.write(" \\n");
writer.write(" \\n");
writer.write("\\n");
catch (IOException e)
throw new RuntimeException("Could not write source for " + index, e);
finally
if (writer != null)
try
writer.close();
catch (IOException e)
//Silent
3.2 JavaPoet
JavaPoet是使用Java的API和面向对象思想来生成.java文件的库。GitHub地址,里面有相当全面的教程。
他的优点是面向对象、可扩展性高,缺点是学习成本高、可读性一般。
我们拿下面的代码举个简单的例子:
// 要生成的代码
package com.example.helloworld;
public final class HelloWorld
public static void main(String[] args)
System.out.println("Hello, JavaPoet!");
而使用JavaPoet生成代码,需要先写方法,然后再写类,再将方法放到类中。
// 使用JavaPoet生成main方法
// public static void main(String[] args)
// System.out.println("Hello, JavaPoet!");
//
MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class, "args")
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
.build();
// 使用JavaPoet生成HelloWorld类,将main方法放入其中
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
// 构建生成文件
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();
javaFile.writeTo(System.out);
总结
最后咱们再总结一下APT。
- APT是JDK提供的工具,用于在编译阶段未生成class之前对源码中的注解进行扫描和处理。
- 获取到注解后可以使用原始方法与JavaPoet生成Java代码。
这样APT的介绍就结束了,希望大家读完这篇文章,会对APT有一个更深入的了解。如果我的文章能给大家带来一点点的福利,那在下就足够开心了。
下次再见!
以上是关于04注解处理器(APT)是什么?——《Android打怪升级之旅》的主要内容,如果未能解决你的问题,请参考以下文章