自定义注解自动代码构建

自定义注解自动代码构建

      • 新建javaLib-annotation(自定义注解)
        • 自定义注解
          • BindViewClass
          • BindFragmentClass
      • 新建javaLib-processor
        • MyProcessor 自定义AbstractProcessor
        • AnnotatedHelper 辅助生成文件
      • app模块配置
      • 编译后生成的文件

新建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;@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);fragments = new HashMap<>();widgets = new HashMap<>();annotatedHelper = new AnnotatedHelper();}@Overridepublic 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;}@Overridepublic 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, int type) {CodeBlock.Builder codeBlock = CodeBlock.builder();codeBlock.beginControlFlow("\tswitch (name)");//这句代码之后添加"{",可单独使用 但建议与"endControlFlow"配对使用for (String key : map.keySet()) {Element element = map.get(key);codeBlock.add("\t\tcase $S: ", key);ClassName className = ClassName.get(element.getEnclosingElement().toString(), element.getSimpleName().toString());if (type ==1){codeBlock.addStatement("return (T) (bundle == null ? new $T() : new $T(bundle))", className, className);//这句执行之后添加";"并换行} else {codeBlock.addStatement("return (T) (bundle == null ? new $T(parent) : new $T(parent,bundle))", className, className);}}codeBlock.addStatement("\t\tdefault: return null");codeBlock.endControlFlow();//这句代码之前添加"}",须有前置"beginControlFlow"配对使用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)//添加方法返回类型.addJavadoc(CodeBlock.builder().add("查找路由表获取fragment对象\n").build())//添加注释.addCode(getCodeBlock(fragments,1).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(getCodeBlock(widgets.get(key),2).build()).addJavadoc(CodeBlock.builder().add("查找路由表获取$S中组件对象\n",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)//类修饰符.addJavadoc(CodeBlock.builder().add("路由表\n").build())//添加注释
//                .addField(FieldSpec  //添加变量
//                        .builder(int.class, "Version", Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL)
//                        .initializer("$L",1)//初始化变量
//                        .build())
//                .addType(TypeSpec.classBuilder("累不累").build())//添加内部类
//                .addAnnotation(Deprecated.class)//添加注解(方法体和变量同理)
//                .addAnnotation(AnnotationSpec.builder(Target.class)//添加注解(方法体和变量同理)
//                        .addMember("value", "$L.TYPE",ClassName.get("java.lang.annotation", "ElementType"))
//                        .build()).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;}}
}