自定義註解自動代碼構建
新建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;
}
}
}