java中常用的一些框架都使用到了注解,比如EventBus、Dragger2、Butterknife等,从这些流行框架可以看出,注解在Android中使用还是很广泛的,常见的注解有运行时注解和编译期注解,运行时注解是通过反射在运行时拿到注解信息进行处理,编译期注解是在编译阶段根据注解生成相应的中间类来处理注解,因为运行时注解需要在运行时执行反射代码和注解处理逻辑,所以它的运行时性能没有编译期注解高,本文学习的注解方式就是APT编译时注解。
1、创建一个名称为annotation的module用来单独存放自定义的注解类,然后在该模块下可以新建一个自定义的注解类(这里以HuiAnnotation为例)
/**
* Created by znh on 2020/7/29
*
* 自定义注解
*/
@Target(ElementType.TYPE)//作用在类上
@Retention(RetentionPolicy.CLASS)//编译期注解
public @interface HuiAnnotation {
}
2、创建一个名称为processor的module用来存放注解处理器,这个模块中需要处理上一步中自定义的注解,所以需要将annotation模块引入进来,自定义的注解处理器需要继承至谷歌注解处理器框架中的AbstractProcessor类,所以需要引入注解处理器框架,为了更好的生成java模板代码,还可以引入javapoet代码模板生成框架(这个不是必须的,使用它可以更方便的生成java代码),以上3个依赖包在gradle中的依赖配置如下:
//添加注解处理器依赖
implementation 'com.google.auto.service:auto-service:1.0-rc6'
//gradle5.0及以上需要
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
//添加自定义的注解类依赖
implementation project(':annotation')
//用来生成java模板代码的框架
implementation 'com.squareup:javapoet:1.11.1'
然后在该模块下新建一个注解处理器类(这里以HuiProcessor为例),这个自定义的注解处理器类中有几个比较重要的方法需要重写:
注解处理器HuiProcessor的代码如下:
/**
* Created by znh on 2020/7/29
*
* 自定义注解处理器
*/
@AutoService(Processor.class)
public class HuiProcessor extends AbstractProcessor {
private Elements mElementUtils;
private Messager mMessager;
/**
* 做一些初始化操作
*
* @param processingEnvironment
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mElementUtils = processingEnvironment.getElementUtils();
mMessager = processingEnvironment.getMessager();
}
/**
* 支持的注解有哪些
*
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotationTypes = new LinkedHashSet<>();
annotationTypes.add(HuiAnnotation.class.getCanonicalName());
return annotationTypes;
}
/**
* 支持的jdk版本
*
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* 注解核心处理方法
*
* @param set
* @param roundEnvironment
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//没有需要处理的注解
if (set.isEmpty()) return false;
//获取项目中使用了HuiAnnotation注解的元素集合
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(HuiAnnotation.class);
//遍历elements
for (Element element : elements) {
//获取元素的包名
String packageName = mElementUtils.getPackageOf(element).getQualifiedName().toString();
//获取元素的类名
String className = element.getSimpleName().toString();
//自定义需要生成的类文件名称
String customClassName = className + "Helper";
//打印日志
mMessager.printMessage(Diagnostic.Kind.NOTE, "packageName:" + packageName);
mMessager.printMessage(Diagnostic.Kind.NOTE, "className:" + className);
mMessager.printMessage(Diagnostic.Kind.NOTE, "customClassName:" + customClassName);
//创建一个方法参数
ParameterSpec msgParam = ParameterSpec
.builder(String.class, "msg") //定义参数类型和参数名称
.build();
//生成方法
MethodSpec print = MethodSpec.methodBuilder("print")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(String.class)
.addStatement("$T.out.println($S)", System.class, "Hello," + customClassName)//定义打印语句
.addParameter(msgParam)
.addStatement("return $N", msgParam)
.build();
//生成类
TypeSpec customClassType = TypeSpec.classBuilder(customClassName)
.addModifiers(Modifier.PUBLIC)
.addMethod(print)
.build();
//生成java文件
JavaFile javaFile = JavaFile.builder(packageName, customClassType).build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
}
3、这个注解处理器的作用是生成一个类名为自定义的customClassName中间辅助类,在这个类中生成一个能打印信息的方法。注解和注解处理器定义完成之后就可以在项目中使用了,在app的module中使用时需要将注解和注解处理器依赖进去,gradle配置如下:
implementation project(':annotation')
annotationProcessor project(':processor')
然后在需要的类上添加注解并编译项目,会生成一个中间辅助类:
然后可以调用生成的中间辅助类的方法去完成自己的业务逻辑了,使用代码如下:
package com.znh.aptdemo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.znh.annotation.HuiAnnotation;
@HuiAnnotation //为MainActivity添加注解
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test();
}
public void test() {
//调用中间类的方法进行测试
String msg = MainActivityHelper.print("测试一下...");
System.out.println(msg);
}
}
打印结果如下:
13077-13077/com.znh.aptdemo I/System.out: Hello,MainActivityHelper
13077-13077/com.znh.aptdemo I/System.out: 测试一下...
Demo地址:https://github.com/huihuigithub/blog_demo_projects (java_apt_demo项目)