Android模块化点滴-JavaPoet

236 阅读2分钟

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

JavaPoet

JavaPoetSquare推出的开源java代码生成框架,提供Java Api生成Java源文件。在注解处理器中,能很方便的生成想要的代码。

JavaPoet常用类

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

JavaPoe字符串格式化规则

  • $L字面量,如:int value = $L,10
  • $S字符串,如:$S,hello
  • $T 类、接口,如:$T,MainActivity
  • $N变量,如:user.$N,name

HelloWorld

看看官网Demo是怎么写的

首先要知道,要做成什么样,下面的HelloWorld类,就是要利用JavaPoet生成的目标。

package com.example.helloworld;

public final class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, JavaPoet!");
  }
}

接下来看看,怎么用JavaPoet

//定义main方法
MethodSpec main = MethodSpec.methodBuilder("main")
    //添加方法权限
    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
    //添加方法返回值
    .returns(void.class)
    //添加方法参数
    .addParameter(String[].class, "args")
    //添加方法体执行语句
    .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
    .build();

//定义HelloWorld类
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
    .addMethod(main)
    .build();

//生成Java文件
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
    .build();

javaFile.writeTo(System.out);

上面注释已经很清楚,可以看出和书写习惯是相反来的,从下往上写,先生成方法,再生成类,最后生成包名,输出为文件。

改造之前APT写入文件的方式的方式

上一篇中,是通过Writer生成文件

     // 创建一个新的源文件(Class),并返回一个对象以允许写入它
                val sourceFile = filer.createSourceFile("$packageName.$finalClassName")
                // 定义Writer对象,开启写入
                val 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注解的path值
                val aRouter: ARouter = it.getAnnotation(ARouter::class.java)
                writer.write(
                    """
            if (path.equals("${aRouter.path}")) {
            
            """.trimIndent()
                )
                writer.write("return $className.class;\n}\n")
                writer.write("return null;\n")
                writer.write("}\n}")

这次改用JavaPoet

先看要生成的代码样子

package com.loveqrc.aptdemo;

public class MainActivityARouter {
    public static Class<?> findTargetClass(String path) {
        return path.equals("app/MainActivity")? MainActivity.class:null;
    }
}

然后添加依赖

// 帮助通过类调用的形式来生成Java代码
implementation "com.squareup:javapoet:1.9.0"
  // 高级写法,javapoet构建工具,参考(https://github.com/JakeWharton/butterknife)
    try {
        // 获取类之上@ARouter注解的path值
        val aRouter: ARouter = it.getAnnotation(ARouter::class.java)

        // 构建方法体
        val method: MethodSpec = MethodSpec.methodBuilder("findTargetClass") // 方法名
            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
            .returns(Class::class.java) // 返回值Class<?>
            .addParameter(String::class.java, "path") // 参数(String path)
            // 方法内容拼接:
            // return path.equals("/app/MainActivity") ? MainActivity.class : null
            .addStatement(
                "return path.equals($S) ? $T.class : null",
                aRouter.path, ClassName.get(it as TypeElement?)
            )
            .build() // 构建

        // 构建类
        val type: TypeSpec = TypeSpec.classBuilder(finalClassName)
            .addModifiers(Modifier.PUBLIC) //, Modifier.FINAL)
            .addMethod(method) // 添加方法体
            .build() // 构建

        // 在指定的包名下,生成Java类文件
        val javaFile: JavaFile = JavaFile.builder(packageName, type)
            .build()
        javaFile.writeTo(filer)
    } catch (e: IOException) {
        e.printStackTrace()
    }


}

Writer生成文件不同,javapoet是操作api来生成文件,从写的复杂度来说javapoet更加复杂些,无论哪种方法,最终目的是生成想要的java文件格式。