赞
踩
apt:注解处理器,通俗来讲:根据规则,帮我们生成代码、类文件。
分module来实现apt。分别为:
Java Library
Java Library
Android Module
java library
,并创建注解类ARouter
/** * <strong>Activity使用的布局文件注解</strong> * <ul> * <li>@Target(ElementType.TYPE) // 接口、类、枚举、注解</li> * <li>@Target(ElementType.FIELD) // 属性、枚举的常量</li> * <li>@Target(ElementType.METHOD) // 方法</li> * <li>@Target(ElementType.PARAMETER) // 方法参数</li> * <li>@Target(ElementType.CONSTRUCTOR) // 构造函数</li> * <li>@Target(ElementType.LOCAL_VARIABLE)// 局部变量</li> * <li>@Target(ElementType.ANNOTATION_TYPE)// 该注解使用在另一个注解上</li> * <li>@Target(ElementType.PACKAGE) // 包</li> * <li>@Retention(RetentionPolicy.RUNTIME) <br>注解会在class字节码文件中存在,jvm加载时可以通过反射获取到该注解的内容</li> * </ul> * <p> * 生命周期:SOURCE < CLASS < RUNTIME * 1、一般如果需要在运行时去动态获取注解信息,用RUNTIME注解 * 2、要在编译时进行一些预处理操作,如ButterKnife,用CLASS注解。注解会在class文件中存在,但是在运行时会被丢弃 * 3、做一些检查性的操作,如@Override,用SOURCE源码注解。注解仅存在源码级别,在编译的时候丢弃该注解 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface ARouter { // 详细路由路径(必填),如 "/app/MainActivity" String path(); // 可以不填写,可以从path中拿 String group() default ""; }
新建java library
,并配置.gradle文件
。注意在gradle文件中高低版本的适配。
apply plugin: 'java-library' dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // 注册注解,并对其生成META-INF的配置信息,rc2在gradle5.0后有坑 // As-3.2.1 + gradle4.10.1-all + auto-service:1.0-rc2 // implementation 'com.google.auto.service:auto-service:1.0-rc2' // As-3.4.1 + gradle5.1.1-all + auto-service:1.0-rc4 compileOnly 'com.google.auto.service:auto-service:1.0-rc4' annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4' // 引入注解,让注解处理器,处理注解 implementation project(':annotation') } // java控制台输出中文乱码 tasks.withType(JavaCompile) { options.encoding = "UTF-8" } // jdk编译版本1.7 sourceCompatibility = "7" targetCompatibility = "7"
自定义注解处理器类
// AutoService则是固定的写法,加个注解即可 // 通过auto-service中的@AutoService可以自动生成AutoService注解处理器,用来注册 // 用来生成 META-INF/services/javax.annotation.processing.Processor 文件 @AutoService(Processor.class) // 允许/支持的注解类型,让注解处理器处理(新增annotation module),支持多个 @SupportedAnnotationTypes(value = {"com.purang.annotation.ARouter"}) // 指定JDK编译版本 @SupportedSourceVersion(value = SourceVersion.RELEASE_7) // 注解处理器接收的参数 @SupportedOptions(value = "content") public class ARouterProcessor extends AbstractProcessor { // 操作Element的工具类 , 类、函数、属性都是Element private Elements mElementUtils; // Type (类信息)的工具类,包含用于操作TypeMirror的工具方法 private Types mTypesUtils; // Messager用来报告错误,警告和其他提示信息 private Messager mMessager; // 文件生成器 类/资源,Filter用来创建新的源文件,class文件以及辅助文件 private Filer mFiler; // 该方法主要用于一些初始化的操作,通过该方法的参数ProcessingEnvironment可以获取一些列有用的工具类 @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); // 父类受保护属性,可以直接拿来使用。 // 其实就是init方法的参数ProcessingEnvironment // processingEnv.getMessager(); //参考源码64行 mElementUtils = processingEnvironment.getElementUtils(); mTypesUtils = processingEnvironment.getTypeUtils(); mMessager = processingEnvironment.getMessager(); mFiler = processingEnvironment.getFiler(); // 通过processingEnvironment去获取build.gradle传过来的参数 String content = processingEnvironment.getOptions().get("content"); // 有坑:Diagnostic.Kind.ERROR,异常会自动结束,不像安卓中Log.e那么好使 mMessager.printMessage(Diagnostic.Kind.NOTE, content); } /** * 相当于main函数,开始处理注解 * 注解处理器的核心方法,处理具体的注解,生成Java文件 * * @param set 使用了支持处理注解的节点集合(类 上面写了注解) * @param roundEnvironment 当前或是之前的运行环境,可以通过该对象查找找到的注解。 * @return true 表示后续处理器不会再处理(已经处理完成) */ @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { if (set.isEmpty()) return false; // 获取所有带ARouter注解的 类节点 Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class); for (Element element : elements) { // 通过类节点获取包节点(全路径:com.netease.xxx) String packageName = mElementUtils.getPackageOf(element).getQualifiedName().toString(); // 获取简单类名 String className = element.getSimpleName().toString(); mMessager.printMessage(Diagnostic.Kind.NOTE, "被注解的类有:" + className); // 最终想生成的类文件名 String finalClassName = className + "$$ARouter"; // 公开课写法,也是EventBus写法(https://github.com/greenrobot/EventBus) try { // 创建一个新的源文件(Class),并返回一个对象以允许写入它 JavaFileObject sourceFile = mFiler.createSourceFile(packageName + "." + finalClassName); // 定义Writer对象,开启写入 Writer writer = sourceFile.openWriter(); // 设置包名 writer.write("package " + packageName + ";\n\n"); writer.write("public class " + finalClassName + "{\n"); writer.write("public static Class<?> findTargetClass(String path){\n"); // 获取类之上@ARouter注解的path值 ARouter annotation = element.getAnnotation(ARouter.class); writer.write("if(path.equalsIgnoreCase(\"" + annotation.path() + "\")){\n"); writer.write("return " + className + ".class;}\n"); writer.write("return null;\n"); writer.write("}\n}"); writer.close(); } catch (IOException e) { e.printStackTrace(); } } return true; } }
依赖注解及注解处理器
// 依赖注解
implementation project(':annotation')
// 依赖注解处理器
annotationProcessor project(':compiler')
在主module的 build.gradle中做一下配置,用于给APT传参
defaultConfig {
// 在gradle文件中配置选项参数值(用于APT传参接收)
// 切记:必须写在defaultConfig节点下
javaCompileOptions {
annotationProcessorOptions {
arguments = [content : 'hello apt']
}
}
}
使用注解
@ARouter(path = "/app/OrderActivity")
public class MainActivity extends AppCompatActivity {
// ...
}
执行命令Clean Project ,Make Project
查看控制台:
在主module 的build
目录下 查看按照规则生成的代码:
可以看到分别在\..\generated\..和\intermediates\javac\..\
目录下生成了java
文件和class
文件 MainActivity$$ARouter
,前者可编辑。
执行命令Build APK(s)
打包生成apk,查看该文件是否在apk中:
随便创建几个类,分别添加ARouter
注解,执行 clean 、 make
命令:
解释一下在注解处理器 module下 ARouterProcessor
文件中@AutoService(Processor.class)
注解的作用(具体查看 <自定义注解处理器类>)。查看注解处理器 module下的目录:\..\compiler\build\classes\java\main\META-INF\services
,生成了注解处理器的清单文件,并将ARouterProcessor
进行了注册。类似于将MainActivity
在AndroidManifest.xml
文件中注册一样。
查看下javax.annotation.processing.Processor
文件:
com.purang.compiler.ARouterProcessor
square
公司出品。1.1 其他配置与在上文中APT的配置一模一样,此外 在注解处理器module
中添加依赖:
dependencies {
// As-3.4.1 + gradle5.1.1-all + auto-service:1.0-rc4
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
implementation 'com.squareup:javapoet:1.11.1'
}
1.2 同官方demo一样,先写一个Hello World
的java类,按以下代码为模板:
package com.example.helloworld;
public final class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, JavaPoet!");
}
}
1.3 编写ARouterProcessor2
文件:
@AutoService(Processor.class) @SupportedAnnotationTypes(value = {"com.purang.annotation.ARouter"}) @SupportedSourceVersion(value = SourceVersion.RELEASE_7) @SupportedOptions(value = {"content"}) public class ARouterProcessor2 extends AbstractProcessor { private Elements mElementsUtils; private Types mTypesUtils; private Messager mMessager; private Filer mFiler; @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); mElementsUtils = processingEnvironment.getElementUtils(); mTypesUtils = processingEnvironment.getTypeUtils(); mMessager = processingEnvironment.getMessager(); String content = processingEnvironment.getOptions().get("content"); mMessager.printMessage(Diagnostic.Kind.NOTE, "传递的参数:" + content); mFiler = processingEnvironment.getFiler(); } @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { if (set.isEmpty()) return false; Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class); for (Element element : elements) { MethodSpec methodSpec = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC).addModifiers(Modifier.STATIC) .addParameter(String[].class, "args") .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!") .build(); TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC).addModifiers(Modifier.FINAL) .addMethod(methodSpec) .build(); JavaFile javaFile = JavaFile.builder("com.purang.compiler", typeSpec) .build(); try { javaFile.writeTo(mFiler); } catch (IOException e) { e.printStackTrace(); } } return false; } }
依次执行Clean Project 、 Make Project
命令,查看build目录:
可以看到生成了HelloWorld.java 和 .class
文件。
1.4 编写需要的ARouterProcessor2
文件
@AutoService(Processor.class) @SupportedAnnotationTypes(value = {"com.purang.annotation.ARouter"}) @SupportedSourceVersion(value = SourceVersion.RELEASE_7) @SupportedOptions(value = {"content"}) public class ARouterProcessor2 extends AbstractProcessor { private Elements mElementsUtils; private Types mTypesUtils; private Messager mMessager; private Filer mFiler; @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); mElementsUtils = processingEnvironment.getElementUtils(); mTypesUtils = processingEnvironment.getTypeUtils(); mMessager = processingEnvironment.getMessager(); String content = processingEnvironment.getOptions().get("content"); mMessager.printMessage(Diagnostic.Kind.NOTE, "传递的参数:" + content); mFiler = processingEnvironment.getFiler(); } @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { if (set.isEmpty()) return false; // 获取所有带ARouter注解的 类节点 Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class); // 遍历所有类节点 for (Element element : elements) { // 通过类节点获取包节点(全路径:com.netease.xxx) String packageName = mElementsUtils.getPackageOf(element).getQualifiedName().toString(); // 获取简单类名 String className = element.getSimpleName().toString(); mMessager.printMessage(Diagnostic.Kind.NOTE, "被注解的类有:" + className); // 最终想生成的类文件名 String finalClassName = className + "$$ARouter"; // 高级写法,javapoet构建工具,参考(https://github.com/JakeWharton/butterknife) try { // 获取类之上@ARouter注解的path值 ARouter aRouter = element.getAnnotation(ARouter.class); // 构建方法体 MethodSpec method = MethodSpec.methodBuilder("findTargetClass") // 方法名 .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(Class.class) // 返回值Class<?> .addParameter(String.class, "path") // 参数(String path) // 方法内容拼接: // return path.equals("/app/MainActivity") ? MainActivity.class : null .addStatement("return path.equals($S) ? $T.class : null", aRouter.path(), ClassName.get((TypeElement) element)) .build(); // 构建 // 构建类 TypeSpec type = TypeSpec.classBuilder(finalClassName) .addModifiers(Modifier.PUBLIC) //, Modifier.FINAL) .addMethod(method) // 添加方法体 .build(); // 构建 // 在指定的包名下,生成Java类文件 JavaFile javaFile = JavaFile.builder(packageName, type) .build(); javaFile.writeTo(mFiler); } catch (IOException e) { e.printStackTrace(); } } return true; } }
依次执行Clean Project 、 Make Project
命令,查看build目录:
1.5 使用到JavaPoet
库的工程有:Retrofit ButterKnife
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。