轉載請標明出處:https:////www.cnblogs.com/tangZH/p/12343786.html,
http://77blogs.com/?p=199
APT 是Annotation Processing Tool 的簡稱,
它是注解處理器,在處理Annotation時可以根據源檔案中的Annotation生成額外的源檔案和其它的檔案(檔案具體內容由Annotation處理器的撰寫者決定),APT還會編譯生成源檔案和原來的源檔案,將它們一起生成class檔案, 簡言之:APT可以根據注解,在編譯時生成代碼,事實上它是javac的一個工具,命令列運行javac后便可以看到:

接下來我們就來實作一個apt的實體,類似于ButterKnife中@BindView注解,基本步驟如下:
1、定義要被處理的注解,
2、定義注解處理器(生成具體的類),
3、呼叫處理器生成的代碼
對應的,我們在工程中需要有這幾個模塊:
1、app,測驗我們的功能
2、apt-annotation,一個Java library module,放置我們自定義注解
3、apt-processor,一個Java library module,注解處理器模塊
4、apt-sdk,一個Android library module,通過反射呼叫apt-processor模塊生成的方法,實作view的系結,
工程目錄如下:

1、在apt-annotation中自定義注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
2、apt-processor中引入依賴,它需要依賴apt-annotation,同時還需要依賴auto-service第三方庫,后面創建注解處理器的時候需要用到,
apt-processor/build.gradle檔案中:
implementation project(':apt-annotation')
implementation 'com.google.auto.service:auto-service:1.0-rc2'
3、在pat-processor中創建注解處理器:
處理器需要繼承AbstractProcessor,注意該module是 java module,如果創建的是android module的話那么就會找不到AbstractProcessor
@AutoService(Processor.class)
@SuppressWarnings("unused")
public class BindViewProcessor extends AbstractProcessor {
private Elements mElementUtils;
private Map<String, ClassCreatorFactory> mClassCreatorFactoryMap = new HashMap<>();
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
//拿到工具類
mElementUtils = processingEnvironment.getElementUtils();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
//這個注解處理器是給哪個注解用的
HashSet<String> supportType = new LinkedHashSet<>();
supportType.add(BindView.class.getCanonicalName());
return supportType;
}
@Override
public SourceVersion getSupportedSourceVersion() {
//回傳java版本
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
mClassCreatorFactoryMap.clear();
//得到所有包含該注解的element集合
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
for (Element element : elements) {
//轉換為VariableElement,VariableElement為element的子類
VariableElement variableElement = (VariableElement) element;
//可以獲取類的資訊的element,也是element的子類
TypeElement classElement = (TypeElement) variableElement.getEnclosingElement();
//獲取包名加類名
String fullClassName = classElement.getQualifiedName().toString();
//保存到集合中
ClassCreatorFactory factory = mClassCreatorFactoryMap.get(fullClassName);
if (factory == null) {
factory = new ClassCreatorFactory(mElementUtils, classElement);
mClassCreatorFactoryMap.put(fullClassName, factory);
}
BindView bindViewAnnotation = variableElement.getAnnotation(BindView.class);
int id = bindViewAnnotation.value();
factory.putElement(id, variableElement);
}
//開始創建java類
for (String key : mClassCreatorFactoryMap.keySet()) {
ClassCreatorFactory factory = mClassCreatorFactoryMap.get(key);
try {
JavaFileObject fileObject = processingEnv.getFiler().createSourceFile(
factory.getClassFullName(), factory.getTypeElement());
Writer writer = fileObject.openWriter();
//寫入java代碼
writer.write(factory.generateJavaCode());
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
}
需要注意的是代碼中不能有中文,否則編譯不通過,我這里為了方便注釋解釋加上了中文,
ClassCreatorFactory的代碼如下,這個類負責提供需要寫入新的類的代碼:
public class ClassCreatorFactory {
private String mBindClassName;
private String mPackageName;
private TypeElement mTypeElement;
private Map<Integer, VariableElement> mVariableElementMap = new HashMap<>();
ClassCreatorFactory(Elements elementUtils, TypeElement classElement) {
this.mTypeElement = classElement;
//PackageElement是element的子類,可以拿到包資訊
PackageElement packageElement = elementUtils.getPackageOf(mTypeElement);
String packageName = packageElement.getQualifiedName().toString();
String className = mTypeElement.getSimpleName().toString();
this.mPackageName = packageName;
//生成的類的名稱
this.mBindClassName = className + "_ViewBinding";
}
public void putElement(int id, VariableElement element) {
mVariableElementMap.put(id, element);
}
public String generateJavaCode() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("/**\n" + " * Auto Created by apt\n" + "*/\n");
stringBuilder.append("package ").append(mPackageName).append(";\n");
stringBuilder.append('\n');
stringBuilder.append("public class ").append(mBindClassName);
stringBuilder.append(" {\n");
generateBindViewMethods(stringBuilder);
stringBuilder.append('\n');
stringBuilder.append("}\n");
return stringBuilder.toString();
}
private void generateBindViewMethods(StringBuilder stringBuilder) {
stringBuilder.append("\tpublic void bindView(");
stringBuilder.append(mTypeElement.getQualifiedName());
stringBuilder.append(" owner) {\n");
for (int id : mVariableElementMap.keySet()) {
VariableElement variableElement = mVariableElementMap.get(id);
String viewName = variableElement.getSimpleName().toString();
String viewType = variableElement.asType().toString();
stringBuilder.append("\t\towner.");
stringBuilder.append(viewName);
stringBuilder.append(" = ");
stringBuilder.append("(");
stringBuilder.append(viewType);
stringBuilder.append(")(((android.app.Activity)owner).findViewById( ");
stringBuilder.append(id);
stringBuilder.append("));\n");
}
stringBuilder.append(" }\n");
}
public String getClassFullName() {
return mPackageName + "." + mBindClassName;
}
public TypeElement getTypeElement() {
return mTypeElement;
}
}
先不談apt-sdk模塊,我們先來看看生成的代碼是怎么樣的,
在app的gradle中引入:
implementation project(':apt-annotation')
annotationProcessor project(':apt-processor')
特別要注意的是apt-processor模塊的依賴引進要用 annotationProcessor,否則編譯報錯
兩個activity中:
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv)
TextView textView;
@BindView(R.id.tv_1)
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
public class Main2Activity extends AppCompatActivity {
@BindView(R.id.tv_2)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
}
}
rebuild一下便可以看到在這個目錄下有我們生成的檔案了,
gradle高版本出現編譯后沒出現檔案的問題,無奈只好降低版本,我使用的版本是gradle 3.1.4 + gralde_wrap gradle-4.4-all.zip

點進入其中一個可以看到是這樣的代碼:
/**
* Auto Created by apt
*/
package com.example.aptsample;
public class MainActivity_ViewBinding {
public void bindView(com.example.aptsample.MainActivity owner) {
owner.tv = (android.widget.TextView)(((android.app.Activity)owner).findViewById( 2131165360));
owner.textView = (android.widget.TextView)(((android.app.Activity)owner).findViewById( 2131165359));
}
}
所以我們只要呼叫bindView就能夠找到該view了,這也是apt-sdk要做的事情,
4、在apt-sdk中創建類,反射呼叫生成的類中的方法
public class DataApi {
public static void bindView(Activity activity) {
Class clazz = activity.getClass();
try {
Class<?> bindViewClass = Class.forName(clazz.getName() + "_ViewBinding");
Method method = bindViewClass.getMethod("bindView", activity.getClass());
method.invoke(bindViewClass.newInstance(),activity);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
5、app的gradle中引入apt-sdk,然后代碼呼叫DataApi的方法
implementation project(':apt-annotation')
annotationProcessor project(':apt-processor')
implementation project(':apt-sdk')
app的MainActivity中實作
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv)
TextView textView;
@BindView(R.id.tv_1)
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DataApi.bindView(this);
tv.setText("a");
}
}
這樣就大功告成了
精美原文:http://77blogs.com/?p=199
原始碼地址:https://github.com/TZHANHONG/AptAutoBindView
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/31654.html
標籤:Android
