APT技術就是得用註解來自動生成一些類式代碼
比如下面這個註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface ARouter {
String path();//如 /app/MainActivity
//可不寫,會在path是截取
String group() default "";
}
@Target裏面的value代表的一些屬性
ElementType.TYPE : 註解在接口,類枚舉之上
ElementType.FIELD : 註解在類的屬性,枚舉常量之上
ElementType.METHOD: 註解在方法之上
ElementType.PARAMETER: 註解在方法參數裏
ElementType.ANOTATION_TYPE: 註解在註解之上
ElementType.PACKAGE: 註解在包上
@Retention 有三種RetentionPolicy.CLASS RetentionPolicy.RUNTIME RetentionPolicy.SOURCE
RetentionPolicy.CLASS: 在編譯時要做一些預處理操作,如butterknife,註解會在class文件中存在,但是在運行時丟棄
RetentionPolicy.RUNTIME: 如果需要在運行時動態獲取註解信息,用這個 反射用的多
RetentionPolicy.SOURCE: 做一些檢查操作 如通常看到的@Override,編譯時就會被丟棄
配置APT環境
首先創建一個java類型的Model,在build.gradle中配置相應的依賴
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
創建下面這樣繼承 AbstractProcessor的類,並重寫二個方法,下面註釋掉的幾個方法可以通過註解配置.
@AutoService(Processor.class)//通過AutoService生成AutoService註解器
@SupportedAnnotationTypes({Constance.AROUTE_ANNOTATION_TYPE, Constance.PARAMETER_ANNOTATION_TYPE})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedOptions({Constance.MODULE_NAME, Constance.APT_PACKAGE})
public class ARouterProcess extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
}
// @Override
// public Set<String> getSupportedAnnotationTypes() {//獲取支持的註解類型
// return super.getSupportedAnnotationTypes();
// }
//
// @Override
// public SourceVersion getSupportedSourceVersion() {//JDK的版本 必填
// return super.getSupportedSourceVersion();
// }
//
// @Override
// public Set<String> getSupportedOptions() {//接收外面傳來的參數,build.gradle裏面配置
// return super.getSupportedOptions();
// }
}
APT主要涉及到的幾個類,需要在init方法裏面初始化
private Elements mElementUtils;
//類信息工具類
private Types mTypesUtils;
//用來日誌輸出的工具類 如警告等
private Messager mMessager;
private Filer mFiler;//文件生成器
在process方法中中可以獲取到所有配置了類註解上的註解的集合
再來了解一下Element這個類,可以說是一個被註解了的節點,可以通過Elements獲取到類的很多信息
比如這個類包節點:
String packageName = mElementUtils.getPackageOf(element).getQualifiedName().toString();
類的簡單名字如: MainActivity
String simpleName = element.getSimpleName().toString();
/想要生成的類文件 如 MainActivity$$Router
String fileClassName = simpleName + "$$ARouter";
上面二個相加就可以得到我們一個類的完成路徑
一般要生成類式代碼就需要一個模板,比如我們在組件化裏面要生成下面這個類式的文件
package com.ancely.zjh.test;
public class XActivity$$ARouter {
public static Class<?> fintTargetClass(String path){
if (path.equalsIgnoreCase("/app/MainActivity")){
return MainActivity.class;
}
return null;
}
}
JavaFileObject sourceFiles = mFiler.createSourceFile(packageName + "." + fileClassName);//創建源文件
開啓一個寫入流
Writer writer = sourceFiles.openWriter();
這樣就可以通過上面這個writer進行一行一行寫內容了
比如第一行package com.ancely.zjh.test;
writer.write("package " + packageName + ";\n");
第二行 public class XActivity$$ARouter {
writer.write("public class " + fileClassName + "{\n");
第三行 public static Class<?> fintTargetClass(String path){
writer.write("public static Class<?> findTargetClass(String path) {" + "\n");
第四行: if (path.equalsIgnoreCase("/app/MainActivity")){
writer.write("if (path.equalsIgnoreCase(\"" + aRouter.path() + "\")){\n");
第五行: return MainActivity.class;
writer.write("return " + simpleName + ".class;" + "\n}");
...
writer.write("return null; \n");
writer.write("} \n");
writer.write("} \n");
最後關閉
writer.close();
這樣自動寫代碼就完成了
avaPoet寫法
需要添加一個依賴
implementation 'com.squareup:javapoet:1.9.0'
先寫方法,方法體,最後寫類,最後寫文件
這裏介紹一下需要用的到常用的類代表了什麼
字符串的一格式化的規則
//javapoet寫法
//先寫方法
MethodSpec findTargetClass = MethodSpec.methodBuilder("findTargetClass") // 方法名 findTargetClass
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)//public static
.returns(Class.class) //需要返回的類型 Class
.addParameter(String.class, "path")//方法裏面的參數類型和參數名 String path
.addStatement("return path.equalsIgnoreCase($S) ? $T.class : null", aRouter.path(), ClassName.get((TypeElement) element))//這裏面就是方法裏的內容
.build();
//再寫類
TypeSpec typeSpec = TypeSpec.classBuilder(fileClassName)
.addModifiers(Modifier.PUBLIC)
.addMethod(findTargetClass)
.build();
//最後寫文件
JavaFile javaFile = JavaFile.builder(packageName, typeSpec).build();
javaFile.writeTo(mFiler);