自定義註解自動代碼構建


新建javaLib-annotation(自定義註解)

build.gradle

apply plugin: 'java-library'
...

//中文亂碼問題(錯誤:編碼GBK不可映射字符)
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}

sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7

自定義註解

@Target:註解的作用目標
@Target(ElementType.TYPE)——接口、類、枚舉、註解
@Target(ElementType.FIELD)——字段、枚舉的常量
@Target(ElementType.METHOD)——方法
@Target(ElementType.PARAMETER)——方法參數
@Target(ElementType.CONSTRUCTOR) ——構造函數
@Target(ElementType.LOCAL_VARIABLE)——局部變量
@Target(ElementType.ANNOTATION_TYPE)——註解
@Target(ElementType.PACKAGE)——包
@Retention:註解的保留位置
RetentionPolicy.SOURCE:這種類型的Annotations只在源代碼級別保留,編譯時就會被忽略,在class字節碼文件中不包含。
RetentionPolicy.CLASS:這種類型的Annotations編譯時被保留,默認的保留策略,在class文件中存在,但JVM將會忽略,運行時無法獲得。
RetentionPolicy.RUNTIME:這種類型的Annotations將被JVM保留,所以他們能在運行時被JVM或其他使用反射機制的代碼所讀取和使用。
@Document:說明該註解將被包含在javadoc中
@Inherited:說明子類可以繼承父類中的該註解

BindViewClass
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface BindViewClass {
    String groupName() default "Widget";//默認值="Widget"

    String value();//無默認值
}

BindFragmentClass
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface BindFragmentClass {
   String value();
}

新建javaLib-processor

build.gradle

apply plugin: 'java-library'

...

dependencies {
    implementation 'com.google.auto.service:auto-service:1.0-rc6'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
    implementation 'com.google.auto:auto-common:0.10'
    implementation 'com.squareup:javapoet:1.11.1'
    implementation project(path: ':annotation')
}

//中文亂碼問題(錯誤:編碼GBK不可映射字符)
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}

sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7

MyProcessor 自定義AbstractProcessor

@SupportedAnnotationTypes({"BindViewClass全路徑","BindFragmentClass全路徑"})
@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {

     private HashMap<String, HashMap<String,Element>> widgets;
    private HashMap<String, Element> fragments;
    private AnnotatedHelper annotatedHelper;


    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        fragments = new HashMap<>();
        widgets = new HashMap<>();
        annotatedHelper = new AnnotatedHelper();
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
       fragments.clear();
        widgets.clear();
        //遍歷所有@BindFragmentClass註解
        for (Element element : roundEnvironment.getElementsAnnotatedWith(BindFragmentClass.class)) {
            BindFragmentClass annotation = element.getAnnotation(BindFragmentClass.class);
            fragments.put(annotation.value(), element);
        }
		//遍歷所有@BindViewClass註解
        for (Element element : roundEnvironment.getElementsAnnotatedWith(BindViewClass.class)) {
            BindViewClass annotation = element.getAnnotation(BindViewClass.class);
            HashMap<String, Element> elementHashMap = widgets.get(annotation.groupName());
            if (elementHashMap == null){
                elementHashMap = new HashMap<>();
            }
            elementHashMap.put(annotation.value(),element);
            widgets.put(annotation.groupName(), elementHashMap);
        }
          //生成文件
		if (!widgets.isEmpty() || !fragments.isEmpty()) {
            try {
                annotatedHelper.createFile(fragments, widgets).writeTo(processingEnv.getFiler());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        types.add(BindFragmentClass.class.getCanonicalName());
        types.add(BindViewClass.class.getCanonicalName());//手動高亮劃重點  聲明要處理的註解
        return types;
    }
}

AnnotatedHelper 輔助生成文件

注 通過ClassName.get("java.lang", "String")設置類型,可自動在文件中導入相關包import java.lang.String;

class AnnotatedHelper {
    private static final TypeVariableName T = TypeVariableName.get("T");//定義泛型

   //生成方法體
    private CodeBlock.Builder getCodeBlock(HashMap<String, Element> map) {
        CodeBlock.Builder codeBlock = CodeBlock.builder();
        codeBlock.add(" switch (name){\n");
        for (String key : map.keySet()) {
            Element element = map.get(key);
            codeBlock.add("     case $S: ", key);
            ClassName className = ClassName.get(element.getEnclosingElement().toString(), element.getSimpleName().toString());
            codeBlock.addStatement("return (T) (bundle == null ? new $T() : new $T(bundle))", className, className);
        }
        codeBlock.add("     default: return null;\n}\n ");
        return codeBlock;
    }

    private CodeBlock.Builder getViewCodeBlock(HashMap<String, Element> map) {
        CodeBlock.Builder codeBlock = CodeBlock.builder();
        codeBlock.add(" switch (name){\n");
        for (String key : map.keySet()) {
            Element element = map.get(key);
            codeBlock.add("     case $S: ", key);
            ClassName className = ClassName.get(element.getEnclosingElement().toString(), element.getSimpleName().toString());
            codeBlock.addStatement("return (T) (bundle == null ? new $T(parent) : new $T(parent,bundle))", className, className);
        }
        codeBlock.add("     default: return null;\n}\n ");
        return codeBlock;
    }

    JavaFile createFile(HashMap<String, Element> fragments, HashMap<String, HashMap<String, Element>> widgets) {
        //生成方法
        MethodSpec.Builder fragmentMethod = MethodSpec.methodBuilder("getFragmentInstance")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)//添加方法修飾信息
                .addTypeVariable(AnnotatedHelper.T)//添加泛型
                .returns(AnnotatedHelper.T)//添加方法返回類型
                .addCode(getCodeBlock(fragments).build())//添加方法體
                .addParameter(ClassName.get("java.lang", "String"), "name")//添加參數  (類型 參數名)
                .addParameter(ClassName.get("android.os", "Bundle"), "bundle");//添加參數  (類型 參數名)

		//生成方法
        ArrayList<MethodSpec.Builder> groups = null;
        if (widgets != null && !widgets.keySet().isEmpty()) {
            groups = new ArrayList<>();
            for (String key : widgets.keySet()) {
                MethodSpec.Builder widgetMethod = MethodSpec.methodBuilder(String.format("get%sInstance", key))
                        .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                        .addTypeVariable(AnnotatedHelper.T)
                        .returns(AnnotatedHelper.T)
                        .addCode(getViewCodeBlock(widgets.get(key)).build())
                        .addParameter(ClassName.get("java.lang", "String"), "name")
                        .addParameter(ClassName.get("android.view", "ViewGroup"), "parent")
                        .addParameter(ClassName.get("android.os", "Bundle"), "bundle");
                groups.add(widgetMethod);
            }
        }
		//生成類
        TypeSpec.Builder injectClass = TypeSpec.classBuilder("Factory")//類名
                .addModifiers(Modifier.PUBLIC)//類修飾符
                .addMethod(fragmentMethod.build());//添加方法
        if (groups != null && !groups.isEmpty()) {
            for (MethodSpec.Builder build : groups) {
                injectClass.addMethod(build.build());
            }
        }
        return JavaFile.builder("指定文件包路徑", injectClass.build()).build();//在指定包路徑生成類文件
    }
}

以上通過JavaFile相關API自動生成文件
以下通過JavaFileObject+字符串寫入文件

StringBuilder builder = new StringBuilder()
                .append("package com.***.view;\n\n")
                .append("import android.os.Bundle;\n")
                ...
                .append("}");

        try {
            JavaFileObject source = processingEnv.getFiler().createSourceFile(
                    "指定文件包路徑.Factory");
            Writer writer = source.openWriter();
            writer.write(builder.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
            //
        }

app模塊配置

build.gradle 本人項目中使用了kotlin所以要用kapt,沒有kotlin的項目中使用annotationProcessor

apply plugin: 'kotlin-kapt'
...
dependencies {
    kapt project(path: ':processor')
    implementation project(path: ':annotation')
 }

編譯後生成的文件

package com.***.view;

import android.os.Bundle;
import android.view.ViewGroup;
import com.***.fragment.HomeFragment;
import com.***.PersonalCentreFragment;
import com.***.SmallAppManagerFragment;
import com.***.WebFragment;
import com.***.BannerVH;
import com.***.DashboardVH;
import com.***.MyAppVH;
import com.***.NotifyVH;
import com.***.WebVH;
import java.lang.String;

public class Factory {
  public static <T> T getFragmentInstance(String name, Bundle bundle) {
     switch (name){
         case "web": return (T) (bundle == null ? new WebFragment() : new WebFragment(bundle));
         case "personalCentre": return (T) (bundle == null ? new PersonalCentreFragment() : new PersonalCentreFragment(bundle));
         case "subApp": return (T) (bundle == null ? new SmallAppManagerFragment() : new SmallAppManagerFragment(bundle));
         case "home": return (T) (bundle == null ? new HomeFragment() : new HomeFragment(bundle));
         default: return null;
    }
     }

  public static <T> T getViewHolderInstance(String name, ViewGroup parent, Bundle bundle) {
     switch (name){
         case "notifyVH": return (T) (bundle == null ? new NotifyVH(parent) : new NotifyVH(parent,bundle));
         case "banner": return (T) (bundle == null ? new BannerVH(parent) : new BannerVH(parent,bundle));
         case "webVH": return (T) (bundle == null ? new WebVH(parent) : new WebVH(parent,bundle));
         case "myApp": return (T) (bundle == null ? new MyAppVH(parent) : new MyAppVH(parent,bundle));
         case "dashboard": return (T) (bundle == null ? new DashboardVH(parent) : new DashboardVH(parent,bundle));
         default: return null;
    }
     }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章