簡(jiǎn)介
APT(Annotation Process Tool)烟瞧,是一種編譯期間處理注解绣溜,按照一定規(guī)則生成代碼的技術(shù)竞漾。
JavaPoet 由square公司開(kāi)源的三方庫(kù)放钦,簡(jiǎn)化生成文件的過(guò)程岩榆。
使用過(guò)程
寫(xiě)一個(gè)BindView為例错负,來(lái)簡(jiǎn)單看一下自動(dòng)生成文件過(guò)程。
工程創(chuàng)建
1.創(chuàng)建一個(gè)正常的Android工程勇边,主模塊為app犹撒,用于寫(xiě)Android業(yè)務(wù)
2.創(chuàng)建一個(gè)純Java模塊,命名為apt-annotation,用于定義注解
3.再創(chuàng)建一個(gè)純Java模塊粒褒,命名為apt-processor,用于解析注解识颊,生成Java文件
gradle配置
app模塊
dependencies {
//依賴(lài)上注解module
implementation project(":apt-annotation")
//依賴(lài)上注解module
annotationProcessor project(":apt-processor")
}
apt-annotation模塊 無(wú)需特殊配置
apt-processor模塊
dependencies {
//依賴(lài)上注解module
implementation project(":apt-annotation")
//谷歌提供的自動(dòng)注冊(cè)服務(wù)
implementation 'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
//生成java代碼庫(kù)
implementation 'com.squareup:javapoet:1.10.0'
}
apt-annotation模塊自定義注解
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
這里定義的BindView用于標(biāo)記字段,值為控件ID
apt-processor模塊實(shí)現(xiàn)自動(dòng)生成代碼的邏輯
定義一個(gè)xxxProcessor繼承自AbstractProcessor奕坟,并為其添加@AutoService注解祥款,如下
@AutoService(Processor.class)
public class BindViewProcessor extends AbstractProcessor {
private Messager messager;
private Elements elementUtils;
private Types types;
private Filer filter;
private Map<String, List<NodeInfo>> mCatch = new HashMap<>();
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filter = processingEnv.getFiler();
types = processingEnv.getTypeUtils();
elementUtils = processingEnv.getElementUtils();
messager = processingEnv.getMessager();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton(BindView.class.getCanonicalName());
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (set == null || roundEnvironment == null) {
return false;
}
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
if (elements == null || elements.isEmpty()) {
return false;
}
//遍歷節(jié)點(diǎn)
for (Element element : elements) {
//獲取節(jié)點(diǎn)包信息
String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString();
//獲取節(jié)點(diǎn)類(lèi)信息,由于@BindView作用于成員屬性上月杉,這里使用getEnclosingElement
String className = element.getEnclosingElement().getSimpleName().toString();
//獲取節(jié)點(diǎn)標(biāo)記的屬性名稱(chēng)
String typeName = element.asType().toString();
//獲取節(jié)點(diǎn)標(biāo)記的屬性名稱(chēng)
String nodeName = element.getSimpleName().toString();
//獲取注解的值
int value = element.getAnnotation(BindView.class).value();
// 打印
messager.printMessage(Diagnostic.Kind.NOTE, "packageName = " + packageName);
messager.printMessage(Diagnostic.Kind.NOTE, "className = " + className);
messager.printMessage(Diagnostic.Kind.NOTE, "typeName = " + typeName);
messager.printMessage(Diagnostic.Kind.NOTE, "nodeName = " + nodeName);
messager.printMessage(Diagnostic.Kind.NOTE, "value = " + value);
//緩存key
String key = packageName + "." + className;
//緩存節(jié)點(diǎn)信息
List<NodeInfo> nodeInfos = mCatch.get(key);
if (nodeInfos == null) {
nodeInfos = new ArrayList<>();
nodeInfos.add(new NodeInfo(packageName, className, typeName, nodeName, value));
//緩存
mCatch.put(key, nodeInfos);
} else {
nodeInfos.add(new NodeInfo(packageName, className, typeName, nodeName, value));
}
}
if (!mCatch.isEmpty()) {
//遍歷臨時(shí)緩存文件
for (Map.Entry<String, List<NodeInfo>> entity : mCatch.entrySet()) {
try {
//創(chuàng)建文件
createFile(entity.getValue());
} catch (Exception e) {
e.printStackTrace();
}
}
}
return false;
}
/**
* 創(chuàng)建文件過(guò)程
*
* @param nodeInfoList
* @return void
**/
private void createFile(List<NodeInfo> nodeInfoList) throws IOException {
NodeInfo nodeInfo = nodeInfoList.get(0);
// 生成文件的類(lèi)名
String className = nodeInfo.getClassName() + "$ViewBinding";
//方法參數(shù)
ParameterSpec parameterSpec = ParameterSpec.builder(ClassName.get(nodeInfo.getPackageName(), nodeInfo.getClassName()), "target").build();
// 方法
MethodSpec.Builder methodSpecBuild = MethodSpec.methodBuilder("bind").addModifiers(Modifier.PUBLIC, Modifier.STATIC).addParameter(parameterSpec).returns(void.class);
//給方法添加代碼塊
for (NodeInfo info : nodeInfoList) {
methodSpecBuild.addStatement("target.$L = ($L)target.findViewById($L)",
info.getNodeName(),
info.getTypeName(),
info.getValue());
}
// 類(lèi)
TypeSpec typeSpec = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC)
.addMethod(methodSpecBuild.build())
.build();
//生成文件
JavaFile.builder(nodeInfo.getPackageName(), typeSpec)
.build()
.writeTo(filter);
}
}
項(xiàng)目會(huì)在編譯的時(shí)候刃跛,掃描@AutoService注解,為其生成代碼
app模塊中使用
先定義一個(gè)ButterKnife
public class ButterKnife {
public static void bind(Activity target){
try {
Class<?> targetClass = target.getClass();
//獲取生成的類(lèi)
Class<?> bindViewClass = Class.forName(targetClass.getName() + "$ViewBinding");
//獲取方法
Method method = bindViewClass.getMethod("bind", targetClass);
//執(zhí)行方法
method.invoke(bindViewClass.newInstance(),target);
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后再activity中使用
public class HomeActivity extends AppCompatActivity {
@BindView(R.id.tv_title)
TextView tvTitle;
@BindView(R.id.tv_content)
TextView tvContent;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
ButterKnife.bind(this);
tvTitle.setText("這是標(biāo)題");
tvContent.setText("這是內(nèi)容主體");
}
}
自動(dòng)生成的文件為
public class HomeActivity$ViewBinding {
public static void bind(HomeActivity target) {
target.tvTitle = (android.widget.TextView)target.findViewById(2131231192);
target.tvContent = (android.widget.TextView)target.findViewById(2131231191);
}
}
自動(dòng)生成的文件就是為了給Activity的字段賦值之用苛萎,因此需要在OnCreate方法的setContentView之后立刻調(diào)用桨昙。
至此检号,JavaPoet的簡(jiǎn)單使用過(guò)程圓滿(mǎn)結(jié)束。
后記蛙酪,剛開(kāi)始學(xué)習(xí)這個(gè)的時(shí)候齐苛,在一個(gè)kotlin的工程里使用的,怎么也生成不了文件滤否,后來(lái)查到脸狸,需要把 annotationProcessor project(":apt-processor") 改成 kapt project(":apt-processor") 參閱這里