Android的一些开源框架,经常利用注解来实现一些功能,这里简单分析下
一、首先是定义注解
在AS中,我们一般是新建Java Library模块,模块名包含annotation关键字,表示在这里定义注解
在build.gradle中添加下面的代码,可以解决java控制台输出中文乱码问题
// java控制台输出中文乱码
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
这里给出一个注解的例子
/**
* 生命周期:SOURCE < CLASS < RUNTIME
* 1、一般如果需要在运行时去动态获取注解信息,用RUNTIME注解
*
* 2、要在编译时进行一些预处理操作,如ButterKnife,用CLASS注解。
* 注解会在class文件中存在,但是在运行时会被丢弃
*
* 3、做一些检查性的操作,如@Override,用SOURCE源码注解。注解仅存在源码级别,在编译的时候丢弃该注解
*/
@Target(ElementType.TYPE) // 该注解作用在类之上
@Retention(RetentionPolicy.CLASS) // 要在编译时进行一些预处理操作,注解会在class文件中存在
public @interface ARouter {
// 详细路由路径(必填),如:"/app/MainActivity"
String path();
// 路由组名(选填,如果开发者不填写,可以从path中截取出来)
String group() default "";
}
当然,此模块中也可以定义一些其他Class类,供注解处理器使用,例如定义RouterBean对象,用来存储注解标记的类信息、path信息和group信息。
二、注解处理器
在AS中仍然需要新建Java Library模块,并在build.gradle文件中添加下面依赖
// 这个不清楚是不是必须的,如果出错就打开
// compileOnly'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor'com.google.auto.service:auto-service:1.0-rc4'
// 帮助我们通过类调用的形式来生成Java代码
implementation "com.squareup:javapoet:1.9.0"
// 引入annotation,处理@ARouter注解
implementation project(':arouter_annotation')
这里必须是Java Library模块,因为使用到 javax包下面的类
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
接下来就是编写注解处理器Processor了,下面给出一个样例:
// AutoService则是固定的写法,加个注解即可
// 通过auto-service中的@AutoService可以自动生成AutoService注解处理器,用来注册
// 用来生成 META-INF/services/javax.annotation.processing.Processor 文件
@AutoService(Processor.class)
// 允许/支持的注解类型,让注解处理器处理, 也可以通过getSupportedAnnotationTypes方法返回
@SupportedAnnotationTypes({"com.xiangxue.arouter_annotation.ARouter"})
// 指定JDK编译版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)
// 注解处理器接收的参数
@SupportedOptions({"moduleName", "packageNameForAPT"})
public class ARouterProcessor extends AbstractProcessor {
// 操作Element的工具类(类,函数,属性,其实都是Element)
private Elements elementTool;
// type(类信息)的工具类,包含用于操作TypeMirror的工具方法
private Types typeTool;
// Message用来打印 日志相关信息
private Messager messager;
// 文件生成器, 类 资源 等,就是最终要生成的文件 是需要Filer来完成的
private Filer filer;
private String moduleName; // (模块传递过来的)模块名 app,personal
private String packageNameForAPT; // (模块传递过来的) 包名
// 允许/支持的注解类型,让注解处理器处理, 也可以通过注解SupportedAnnotationTypes指定
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotataions = new LinkedHashSet<String>();
annotataions.add(ARouter.class.getCanonicalName());
return annotataions;
}
// 做初始化工作
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
elementTool = processingEnvironment.getElementUtils();
messager = processingEnvironment.getMessager();
filer = processingEnvironment.getFiler();
typeTool = processingEnvironment.getTypeUtils();
moduleName = processingEnvironment.getOptions().get("moduleName");
packageNameForAPT = processingEnvironment.getOptions().get("packageNameForAPT");
if (options != null && aptPackage != null) {
messager.printMessage(Diagnostic.Kind.NOTE, "APT 环境搭建完成....");
} else {
messager.printMessage(Diagnostic.Kind.NOTE, "APT 环境有问题,请检查传递的options参数...");
}
}
/**
* 相当于main函数,开始处理注解
* 注解处理器的核心方法,处理具体的注解,生成Java文件
*
* @param set 使用了支持处理注解的节点集合
* @param roundEnvironment 当前或是之前的运行环境,可以通过该对象查找的注解。
* @return true 表示后续处理器不会再处理(已经处理完成)
*/
@Override
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
return false;
}
三、使用注解和注解处理器
在Android的module中,要使用注解和注解处理器,需要在build.gradle中添加下面的依赖和配置选项参数值:
android{
defaultConfig{
// 在gradle文件中配置选项参数值(用于APT传参接收)
// 同学们注意:切记:必须写在defaultConfig节点下
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName(), packageNameForAPT: "packageNameForAPT"]
}
}
}
}
// 注解模块
implementation project(":arouter_annotation")
// 注解处理器
annotationProcessor project(':arouter_compiler')
在编译时期,会扫码注解,运行注解处理器,执行相关操作,例如生成路由表等。
四、注解处理器生成Java文件
这里可以直接一行一行以字符串的形式,拼接出java代码,然后写到文件,也可以采用javapoet库,以面向对象的形式来实现。
eventbus选择了第一种方式,可以参考了解下,不过javapoet是趋势。具体javapoet可参考https://github.com/square/javapoet
为了快速观察和试验javapoet的效果,可以在注解处理器module中编写测试代码,把生成的结果输出到终端:
public class Test {
public static void main(String[] args){
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();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();
try {
javaFile.writeTo(System.out);
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意,这里是Java Library模块,引用了javax 和 javapoet
网友评论