先创建名为 AnnotationDemo 工程,如果已有工程可直接跳过:
工程内新建名为 annotation 的模块。
此模块存放所有注解类,示例注解类名名为 XAnnotation 。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface XAnnotation {
}
因为此注解可用于修饰类,所以目标类型为: ElementType.TYPE ,并且注解信息在运行时保留。
另外新建模块 annotation_processor 用于存放注解处理器。存放注解处理器的模块必须和注解类模块分离,不然上层模块依赖后会出现问题。
创建之后模块布局就是这样:
然后,在模块里添加 javapoet 或 kotlinpoet 依赖,推荐使用 javapoet 。还要导入上述创建的 annotation 模块,这样注解处理器就能识别我们声明的所有注解类。
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.squareup:javapoet:1.11.1'
implementation project(":annotation")
}
sourceCompatibility = "8"
targetCompatibility = "8"
javapoet或 kotlinpoet 的功能是通过代码编写,在编译前用预设代码拼接出新的类。根据名字就知道他们分别能生成java代码和kotlin代码。
创建注解处理器:
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({"com.phantomvk.annotation.XAnnotation"})
public class XProcessor extends AbstractProcessor {
private Filer mFiler;
private Messager mMessager;
private Elements mElementUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnvironment.getFiler();
mMessager = processingEnvironment.getMessager();
mElementUtils = processingEnvironment.getElementUtils();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
types.add(XAnnotation.class.getCanonicalName());
return types;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_8;
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 返回false编译不会通过,必须把输入的代码输出到目标目录
return false;
}
}
继续完善 process() 的逻辑:
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(XAnnotation.class);
for (Element element : elements) {
PackageElement packageElement = mElementUtils.getPackageOf(element);
String packageName = packageElement.getQualifiedName().toString();
MethodSpec methodSpec = MethodSpec.methodBuilder("showLog")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addStatement("$T.out.println($S)", System.class, "Hello JavaPoet.")
.build();
TypeSpec typeSpec = TypeSpec.classBuilder("XGeneratedClass")
.addModifiers(Modifier.PUBLIC)
.addMethod(methodSpec)
.build();
JavaFile javaFile = JavaFile.builder(packageName, typeSpec)
.build();
try {
javaFile.writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
注册上述注解处理器:
注册声明文件必须按照上述目录存放,而且文件名为这个:
javax.annotation.processing.Processor
文件内容的注解处理器的全路径名:
最后在App工程导入注解声明库和注解处理器库:
apply plugin: 'kotlin-kapt'
// JavaPoet required Java8
android {
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}
dependencies {
// ....
implementation project(":annotation")
kapt project(":annotation_processor") // annotationProcessor for Java
}
导入之后就可以用声明好的注解类修饰 MainActivity :
@XAnnotation
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
点击一下构建,就会按照注解类能获取的信息生成新的类 XGeneratedClass :
同时项目也能引用生成类:
@XAnnotation
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
XGeneratedClass.showLog() // 直接引用
}
}
运行之后控制台就能打印:
com.phantomvk.annotationdemo I/System.out: Hello JavaPoet.
上述文章完成声明和开发注解处理器最基本的方式,后续将在此基础上继续增强注解处理器的能力。
完整工程源码: phantomVK/AnnotationDemo
上一篇
优化应用启动内存