先创建名为 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
上一篇
优化应用启动内存