前言
上一篇文章簡單介紹了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字符串格式化規則
核心原理
// 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