🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] > APT(Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。 ![](https://img.kancloud.cn/5c/1d/5c1d64f5d500aa0e0ccc1bdb222d43ee_600x362.png) ## annotationProcessor 与 android-apt **annotationProcessor** 是APT工具中的一种,他是google开发的内置框架,可以直接在build.gradle文件中使用,推荐大家使用 **android-apt** 是由一位开发者自己开发的apt框架,源代码托管在[这里](https://bitbucket.org/hvisser/android-apt),目前不推荐大家使用了 ## 整体设计 一般基于APT实现一个框架,项目目录一般分为三个部分 1. xxxx-annotation:该框架所使用的全部注解,及其相关类 2. xxxx-compiler:注解编译处理器,引入“xxxx-annotation”,在编译器把注解标注的相关目标类生成映射文件 3. xxxx-api/xxxx-library:功能的具体实现以及暴露给外面的api 下面我们仔细说是xxxx-compiler。 ## AbstractProcessor ~~~ @AutoService(Processor.class) public class MyProcessor extends AbstractProcessor { @Override public synchronized void init(ProcessingEnvironment env) { } @Override public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { } @Override public Set<String> getSupportedAnnotationTypes() { } @Override public SourceVersion getSupportedSourceVersion( ~~~ ### init init(ProcessingEnvironment env): 【核心】 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。 ProcessingEnviroment提供很多有用的工具类Elements,Types和Filer。后面我们将看到详细的内容  * Filer是个接口,支持通过注解处理器创建新文件  * Elements 元素操作辅助工具类 ### process process(Set annotations, RoundEnvironment env):【核心】 这相当于每个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。 输入参数annotations 请求处理的注解类型集合 输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素,相当于“有关全局源码的上下文环境”。后面我们将看到详细的内容。 @return 如果返回 true,则这些注解已声明并且不要求后续 Processor 处理它们;如果返回 false,则这些注解未声明并且可能要求后续 Processor 处理它们 ### getSupportedAnnotationTypes 这里你必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。 换句话说,你在这里定义你的注解处理器注册到哪些注解上。 ### getSupportedSourceVersion 用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported()。 ### 例子 ~~~ @AutoService(Processor.class) public class BindViewProcessor extends AbstractProcessor { private Messager mMessager; private Elements mElementUtils; private Map<String, ClassCreatorProxy> mProxyMap = new HashMap<>(); @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); mMessager = processingEnv.getMessager(); mElementUtils = processingEnv.getElementUtils(); } @Override public Set<String> getSupportedAnnotationTypes() { HashSet<String> supportTypes = new LinkedHashSet<>(); supportTypes.add(BindView.class.getCanonicalName()); return supportTypes; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { mMessager.printMessage(Diagnostic.Kind.NOTE, "processing..."); mProxyMap.clear(); //得到所有的注解 Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class); for (Element element : elements) { VariableElement variableElement = (VariableElement) element; TypeElement classElement = (TypeElement) variableElement.getEnclosingElement(); String fullClassName = classElement.getQualifiedName().toString(); ClassCreatorProxy proxy = mProxyMap.get(fullClassName); if (proxy == null) { proxy = new ClassCreatorProxy(mElementUtils, classElement); mProxyMap.put(fullClassName, proxy); } BindView bindAnnotation = variableElement.getAnnotation(BindView.class); int id = bindAnnotation.value(); proxy.putElement(id, variableElement); } //通过遍历mProxyMap,创建java文件 for (String key : mProxyMap.keySet()) { ClassCreatorProxy proxyInfo = mProxyMap.get(key); try { mMessager.printMessage(Diagnostic.Kind.NOTE, " --> create " + proxyInfo.getProxyClassFullName()); JavaFileObject jfo = processingEnv.getFiler().createSourceFile(proxyInfo.getProxyClassFullName(), proxyInfo.getTypeElement()); Writer writer = jfo.openWriter(); writer.write(proxyInfo.generateJavaCode()); writer.flush(); writer.close(); } catch (IOException e) { mMessager.printMessage(Diagnostic.Kind.NOTE, " --> create " + proxyInfo.getProxyClassFullName() + "error"); } } mMessager.printMessage(Diagnostic.Kind.NOTE, "process finish ..."); return true; ~~~ 通过roundEnvironment.getElementsAnnotatedWith(BindView.class)得到所有注解elements,然后将elements的信息保存到mProxyMap中,最后通过mProxyMap创建对应的Java文件,其中mProxyMap是ClassCreatorProxy的Map集合。 ClassCreatorProxy是创建Java代码的代理类,如下: ~~~ public class ClassCreatorProxy { private String mBindingClassName; private String mPackageName; private TypeElement mTypeElement; private Map<Integer, VariableElement> mVariableElementMap = new HashMap<>(); public ClassCreatorProxy(Elements elementUtils, TypeElement classElement) { this.mTypeElement = classElement; PackageElement packageElement = elementUtils.getPackageOf(mTypeElement); String packageName = packageElement.getQualifiedName().toString(); String className = mTypeElement.getSimpleName().toString(); this.mPackageName = packageName; this.mBindingClassName = className + "_ViewBinding"; } public void putElement(int id, VariableElement element) { mVariableElementMap.put(id, element); } /** * 创建Java代码 * @return */ public String generateJavaCode() { StringBuilder builder = new StringBuilder(); builder.append("package ").append(mPackageName).append(";\n\n"); builder.append("import com.example.gavin.apt_library.*;\n"); builder.append('\n'); builder.append("public class ").append(mBindingClassName); builder.append(" {\n"); generateMethods(builder); builder.append('\n'); builder.append("}\n"); return builder.toString(); } /** * 加入Method * @param builder */ private void generateMethods(StringBuilder builder) { builder.append("public void bind(" + mTypeElement.getQualifiedName() + " host ) {\n"); for (int id : mVariableElementMap.keySet()) { VariableElement element = mVariableElementMap.get(id); String name = element.getSimpleName().toString(); String type = element.asType().toString(); builder.append("host." + name).append(" = "); builder.append("(" + type + ")(((android.app.Activity)host).findViewById( " + id + "));\n"); } builder.append(" }\n"); } public String getProxyClassFullName() { return mPackageName + "." + mBindingClassName; } public TypeElement getTypeElement() { return mTypeElement; ~~~ 效果生成 ~~~ public class MainActivity_ViewBinding { public void bind(com.example.gavin.apttest.MainActivity host) { host.mButton = (android.widget.Button) (((android.app.Activity) host).findViewById(2131165218)); host.mTextView = (android.widget.TextView) (((android.app.Activity) host).findViewById(2131165321)); } } ~~~ ## javapoet 上面在ClassCreatorProxy中,通过StringBuilder来生成对应的Java代码。这种做法是比较麻烦的,还有一种更优雅的方式,那就是javapoet。 ~~~ public class ClassCreatorProxy { //省略部分代码... /** * 创建Java代码 * @return */ public TypeSpec generateJavaCode2() { TypeSpec bindingClass = TypeSpec.classBuilder(mBindingClassName) .addModifiers(Modifier.PUBLIC) .addMethod(generateMethods2()) .build(); return bindingClass; } /** * 加入Method */ private MethodSpec generateMethods2() { ClassName host = ClassName.bestGuess(mTypeElement.getQualifiedName().toString()); MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC) .returns(void.class) .addParameter(host, "host"); for (int id : mVariableElementMap.keySet()) { VariableElement element = mVariableElementMap.get(id); String name = element.getSimpleName().toString(); String type = element.asType().toString(); methodBuilder.addCode("host." + name + " = " + "(" + type + ")(((android.app.Activity)host).findViewById( " + id + "));"); } return methodBuilder.build(); } public String getPackageName() { return mPackageName; ~~~ 最后在 BindViewProcessor中 ~~~ @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { //省略部分代码... //通过javapoet生成 for (String key : mProxyMap.keySet()) { ClassCreatorProxy proxyInfo = mProxyMap.get(key); JavaFile javaFile = JavaFile.builder(proxyInfo.getPackageName(), proxyInfo.generateJavaCode2()).build(); try { // 生成文件 javaFile.writeTo(processingEnv.getFiler()); } catch (IOException e) { e.printStackTrace(); } } mMessager.printMessage(Diagnostic.Kind.NOTE, "process finish ..."); return true; } ~~~ ## 参考资料 [深入理解Java:注解(Annotation)–编译时注解的处理](https://blog.csdn.net/fei20121106/article/details/73742537) [你必须知道的APT、annotationProcessor、android-apt、Provided、自定义注解](https://blog.csdn.net/xx326664162/article/details/68490059) [【Android】APT](https://www.jianshu.com/p/7af58e8e3e18) [javapoet](https://github.com/square/javapoet) [ANNOTATION PROCESSING](http://hannesdorfmann.com/annotation-processing/annotationprocessing101)