赞
踩
什么是APT?全称是Annotation Processing Tool,翻译过来就是注解处理工具,它的作用就是可以在代码编译期间对注解进行处理,并且生成Java文件,减少手动的代码输入,因此它能够使我们编写的代码更加优雅。目前很多优秀的第三方库就是使用APT的技术,比如butterknife、retrofit、enentBus等。
创建注解类,命名ARouter.java。
/**
* <pre>
* author : QB
* time : 2019/7/15
* version : v1.0.0
* desc : 注解文件,提供path参数
* </pre>
*/
@Target(ElementType.TYPE)// 该注解作用在类之上
@Retention(RetentionPolicy.CLASS) // 要在编译时进行一些预处理操作,注解会在class文件中存在
public @interface ARouter {
String path();
}
在当前library的build.gradle中添加如下依赖:
// AS3.4.1 + Gradle5.1.1 + 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'
//javaPoet依赖
implementation 'com.squareup:javapoet:1.11.1'
// 引入annotation,处理@ARouter注解
implementation project(':annotation')
创建ARouterCompiler.java类,继承至AbstractProcessor,代码如下:
/** * <pre> * author : QB * time : 2019/7/15 * version : v1.0.0 * desc : 采用javaPoet生成文件 * </pre> */ //使用AutoService生成注解处理器 @AutoService(Processor.class) //当前注解类型 @SupportedAnnotationTypes({"com.xinyartech.annotation.ARouter"}) //JDK版本 @SupportedSourceVersion(SourceVersion.RELEASE_7) //接收参数 @SupportedOptions("content") public class ARouterCompiler extends AbstractProcessor { //节点信息 private Elements elementUtils; //文件生成器 private Filer mFiler; //日志打印 private Messager mMessager; private Types typeUtils; @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); mMessager = processingEnvironment.getMessager(); mFiler = processingEnvironment.getFiler(); elementUtils = processingEnvironment.getElementUtils(); typeUtils = processingEnvironment.getTypeUtils(); mMessager.printMessage(Diagnostic.Kind.NOTE, processingEnvironment.getOptions().get( "content")); } @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) { //获取完整包名 String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString(); //获取类名 String className = element.getSimpleName().toString(); //打印当前类信息 mMessager.printMessage(Diagnostic.Kind.NOTE, "当前ARouter注解的类:" + className); //最终编译生成的java文件 String finalClassName = className + "$$ARouter"; //拿到当前类的注解 ARouter aRouter = element.getAnnotation(ARouter.class); String path = aRouter.path(); //采用拼接的方式生成注解文件 try { //创建java文件 用于处理业务代码 JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + finalClassName); //返回可操作java文件的对象 Writer writer = sourceFile.openWriter(); //设置包名 writer.write("package " + packageName + ";\n"); //设置类名 writer.write("public class " + finalClassName + " {\n"); //添加方法 writer.write("public static Class<?> findTargetClass(String path){\n"); //拿到类注解 ARouter aRouter = element.getAnnotation(ARouter.class); //拿到当前注解的路径 String aRouterPath = aRouter.path(); //以下为方法逻辑 writer.write("if(path.equalsIgnoreCase(\"" + aRouterPath + "\")){\n"); writer.write("return " + className + ".class;\n}\n"); writer.write("return null;\n"); writer.write("}\n}"); //切记不要忘记关闭write writer.close(); } catch (IOException e) { e.printStackTrace(); } return true; } }
在app的build.gradle中添加如下依赖
//注解依赖
implementation project(path: ':annotation')
//依赖注解处理器
annotationProcessor project(':compiler')
如果需要传递参数给注解处理器,比如我们在注解处理器接受的key为"content",那么可以在app的build.gradle配置传递的值,代码如下:
defaultConfig {
...
// 在gradle文件中配置选项参数值(用于APT传参接收)
// 切记:必须写在defaultConfig节点下
javaCompileOptions {
annotationProcessorOptions {
arguments = [content : 'hello javaPoet']
}
}
}
切记,一定要在defaultConfig节点下配置。
@ARouter(path = "/main/MainActivity")
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void jump(View view) {
startActivity(new Intent(this, SecondActivity$$ARouter.getTargetClass("/main" +
"/SecondActivity")));
}
}
@ARouter(path = "/main/SecondActivity")
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
}
}
在类上添加ARouter注解,此时编译项目,会在build目录下生成目标文件。截图如下:
这样就可以像代码中那样实现跳转了。
JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件。这个框架功能非常有用,我们可以很方便的使用它根据注解、数据库模式、协议格式等来对应生成代码。通过这种自动化生成代码的方式,可以让我们用更加简洁优雅的方式要替代繁琐冗杂的重复工作。
//javaPoet依赖
implementation 'com.squareup:javapoet:1.11.1'
主要的代码在process()方法中。代码如下:
@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) { //获取完整包名 String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString(); //获取类名 String className = element.getSimpleName().toString(); //打印当前类信息 mMessager.printMessage(Diagnostic.Kind.NOTE, "当前ARouter注解的类:" + className); //最终编译生成的java文件 String finalClassName = className + "$$ARouter"; //拿到当前类的注解 ARouter aRouter = element.getAnnotation(ARouter.class); String path = aRouter.path(); //文件处理器编写代码 ,采用javaPoet方式 先写方法,在写类,最后写包 method->class->package //构建方法 $T:代表类 $S:代表字符串 MethodSpec methodSpec = MethodSpec.methodBuilder("getTargetClass") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(Class.class) .addParameter(String.class, "path") .addStatement("return path.equalsIgnoreCase($S) ? " + "$T.class : null", path, ClassName.get((TypeElement) element)) .build(); //构建类 TypeSpec typeSpec = TypeSpec.classBuilder(finalClassName) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(methodSpec) .build(); //构建包 JavaFile javaFile = JavaFile.builder(packageName, typeSpec) .build(); //写入到文件生成器 try { javaFile.writeTo(mFiler); } catch (IOException e) { e.printStackTrace(); } } return true; }
重新编译,即可达到同样的效果。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。