組件化2---APT的實現(ARoute)--傳統寫法和JavaPoet寫法

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);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章