Android组件化实战五: APT的高级用法JavaPoet

前言

上一篇文章简单介绍了APT及其使用,生成相应的java文件,帮我们执行相关的操作,生成java文件的方式是字符串拼接的方式,但是如果要生成的java文件成员属性和方法比较多,这种方式就比较麻烦了,而且容易出现人为失误。所有就有了API调用的方式生成java文件,也就是JavaPoet。

什么是JavaPoet

项目主页及源码:https://github.com/square/javapoet

APT + JavaPoet = 超级利刃

JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件,这个框架功能非常实用,也是我们习惯的Java面向对象OOP语法,可以很方便的使用它根据注解生成相应的代码,通过这种自动化生成代码的方式,可以让我们更加简洁优雅的方式要替代繁琐冗杂的重复工作(常规的通过继承注解处理器AbstractProcessor,根据注解生成java代码的时候,需要一行一行的进行字符串的拼接,如果生成java文件内容较多,实在是不优雅,可以说是不人性)。

结构体语言

Android Studio 3.4.2 + Gradle5.1.1 (向下兼容)

compileOnly ‘com.google.auto.service:auto-service:1.0-rc4’
annotationProcessor ‘com.google.auto.service:auto-service:1.0-rc4’

依赖

//帮助我们通过类调用的形式来生成java代码

implementation ‘com.squareup:javapoet:1.10.0’

JavaPoet的8个常用类

注意与APT中结构体语言表述区别

类对象 说明
MethodSpec 代表一个构造函数或方法说明
TypeSpec 代表一个类、接口或者枚举声明
FieldSpec 代表一个成员变量,一个字段声明
JavaFile 包含一个顶级类的java文件
ParameterSpec 用来创建参数
AnnotationSpec 用来创建注解
ClassName 用来包装一个类
TypeName 类型,如在添加返回值类型时使用TypeName.VOID

JavaPoet字符串格式化规则

Android组件化实战五: APT的高级用法JavaPoet_第1张图片

核心原理

// AutoService则是固定的写法,加个注解即可
// 通过auto-service中的@AutoService可以自动生成AutoService注解处理器,用来注册
// 用来生成 META-INF/services/javax.annotation.processing.Processor 文件
@AutoService(Processor.class)
// 允许/支持的注解类型,让注解处理器处理(新增annotation module)
@SupportedAnnotationTypes({"com.example.annotation.ARouter"})
// 指定JDK编译版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)
// 注解处理器接收的参数
@SupportedOptions("content")
public class ARouterProcessor extends AbstractProcessor {

    // 操作Element工具类 (类、函数、属性都是Element)
    private Elements elementUtils;

    // type(类信息)工具类,包含用于操作TypeMirror的工具方法
    private Types typeUtils;

    // Messager用来报告错误,警告和其他提示信息
    private Messager messager;

    // 文件生成器 类/资源,Filter用来创建新的源文件,class文件以及辅助文件
    private Filer filer;

    /**
     *该方法主要用于一些初始化的操作,通过该方法的参数ProcessingEnvironment可以获取一些列有用的
     *工具类
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        // 父类受保护属性,可以直接拿来使用。
        // 其实就是init方法的参数ProcessingEnvironment
        // processingEnv.getMessager(); //参考源码64行
        elementUtils = processingEnvironment.getElementUtils();
        messager = processingEnvironment.getMessager();
        filer = processingEnvironment.getFiler();

        // 通过ProcessingEnvironment去获取build.gradle(app module)传过来的参数
        String content = processingEnvironment.getOptions().get("content");
        // 有坑:Diagnostic.Kind.ERROR,异常会自动结束,不像安卓中Log.e那么好使
        messager.printMessage(Diagnostic.Kind.NOTE, content);
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {
        if (set.isEmpty()) return false;
        //获取所有被@QRouter注解的 类节点
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(ARouter.class);
        //遍历
        for (Element element : elements) {
            //类节点的上一节点,包节点(获取包名)
            String packageName =
            elementUtils.getPackageOf(element).getQualifiedName().toString();
            //获取简单类名(不带包名)
            String className = element.getSimpleName().toString();
            //打印类名信息
            messager.printMessage(Diagnostic.Kind.NOTE, "被@ARouter注解的类有:" + className);
            //最终生成的类文件名
            String finalClassName = className + "$ARouter";
            //获取注解
            ARouter aRouter = element.getAnnotation(ARouter.class);

            //构建方法体

            //public static Class findTargetClass(String path) {}
            MethodSpec methodSpec = 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(),//$S 参数值, 字符串 /app/MainActivity
                            ClassName.get((TypeElement) element))//$T 类名
                    .build();//构建
            //构建类
            TypeSpec typeSpec = TypeSpec.classBuilder(finalClassName)
                    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                    .addMethod(methodSpec)//添加方法体
                    .build();//构建

            //生成文件
            JavaFile javaFile = JavaFile.builder(packageName, typeSpec)
                    .build();

            try {
                javaFile.writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        return true;
    }
}

build项目,生成的java文件如下:

public final class MainActivity$ARouter {
    public static Class findTargetClass(String path) {
        return path.equals("/app/MainActivity") ? MainActivity.class : null;
    }
}

可对比上一篇文章字符串拼接的方式生成java文件。
代码链接:https://github.com/xpf-android/Moduar_JavaPoet

你可能感兴趣的:(APT,JavaPoet,组件化)