上次已经分析了 ButterKnife 源码分析
准备工作
-
butterknife ( Android Library )
绑定 -
butterknife - annotation ( Java Library )
自定义注解 -
butterknife - compiler ( Java Library )
注解处理器
1、处理注解
@Retention(RetentionPolicy.CLASS)//生命周期
@Target(ElementType.FIELD)//类型
public @interface BindView {
int value();
}
2、注解处理器
@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
//文件
Filer filer;
//节点工具类
Elements elements;
static final String superClassPackage = "com.darren.butterknife";
static final String unBinder = "UnBinder";
static final String util = "Utils";
//初始化工具
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
elements = processingEnv.getElementUtils();
}
//设置支持的注解
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new HashSet<>();
types.add(BindView.class.getCanonicalName());
return types;
}
//支持的版本
@Override
public SourceVersion getSupportedSourceVersion() {
return processingEnv.getSourceVersion();
}
//核心方法
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//获取当前源码中所有使用 @BindView 的变量节点
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);
//存储每个 Activity 对应的节点
Map<TypeElement, List<VariableElement>> map = new HashMap<>();
//便利 Set 集合
for (Element element : elements) {
//变量节点
VariableElement variableElement = (VariableElement) element;
//类节点
TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
//根据类节点获取属性节点
List<VariableElement> variableElementList = map.get(typeElement);
//判断当前的类节点对应的属性节点集合
if (variableElementList == null) {
variableElementList = new ArrayList<>();
map.put(typeElement, variableElementList);
}
variableElementList.add(variableElement);
}
//便利 Activity 中的节点,通过 JavaPoet 生成 Java 文件
for (Map.Entry<TypeElement, List<VariableElement>> entry : map.entrySet()) {
//获取类节点
TypeElement typeElement = entry.getKey();
//获取变量节点列表
List<VariableElement> variableElements = entry.getValue();
//获取类名
String activityName = getClassName(typeElement);
//获取包名
String packageName = getPackageName(typeElement);
//父类
ClassName superClass = ClassName.get(superClassPackage, unBinder);
//当前类
ClassName className = ClassName.bestGuess(activityName);
//创建类并继承UnBinder
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(activityName + "_ViewBinding")
.addModifiers(Modifier.PUBLIC)
.addSuperinterface(superClass)
.addField(className, "target", Modifier.PRIVATE);
//创建 unbind 方法
MethodSpec.Builder unbindBuilder = MethodSpec.methodBuilder("unbind")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC);
unbindBuilder.addStatement("$T target = this.target", className);
unbindBuilder.addStatement("if (target == null) throw new IllegalStateException(\"Bindings already cleared.\")");
unbindBuilder.addStatement("this.target = null");
//创建构造方法
ClassName uiThreadClassName = ClassName.get("androidx.annotation", "UiThread");
MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
.addAnnotation(uiThreadClassName)
.addParameter(className, "target", Modifier.FINAL)
.addModifiers(Modifier.PUBLIC);
constructorBuilder.addStatement("this.target = target");
ClassName utilClass = ClassName.get(superClassPackage, util);
//便利变量集合,在构造方法中完成 findViewById 逻辑
for (VariableElement variableElement : variableElements) {
//通过注解拿到 id
int id = variableElement.getAnnotation(BindView.class).value();
//获取变量名
String fileName = variableElement.getSimpleName().toString();
//$L for Literals 替换字符串
//$T for Types 替换类型,可以理解成对象
constructorBuilder.addStatement("target.$L = $T.findViewById(target,$L,$T.class)", fileName, utilClass, id,variableElement.asType());
unbindBuilder.addStatement("target.$L = null", fileName);
}
//添加方法
classBuilder.addMethod(unbindBuilder.build());
classBuilder.addMethod(constructorBuilder.build());
//将 Java 写成 Class 文件
try {
JavaFile.builder(packageName, classBuilder.build())
.addFileComment("ButterKnifeProcessor")
.build()
.writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
//获取类名
private String getClassName(TypeElement typeElement) {
String className = typeElement.getSimpleName().toString();
return className;
}
//获取包名
private String getPackageName(TypeElement typeElement) {
String packageName = elements.getPackageOf(typeElement).toString();
return packageName;
}
}
MainActivity_ViewBinding以上步骤完成就可以在 build 目录下找到对应的 ViewBinding
3、绑定
public class ButterKnife {
public static UnBinder bind(Activity activity) {
try {
Class<?> clazz = Class.forName(activity.getClass().getName() + "_ViewBinding");
Constructor<?> cons = clazz.getConstructor(activity.getClass());
UnBinder unbinder = (UnBinder) cons.newInstance(activity);
return unbinder;
} catch (Exception e) {
e.printStackTrace();
}
return UnBinder.EMPTY;
}
}
4、总结
第一步:处理自定义注解
标明注解的生命周期和类型
第二步:处理 Processor
init 方法:初始化工具类
getSupportedAnnotationTypes 方法:设置支持的注解
process 方法:便利注解,使用 JavaPoet 生成 java 文件,并转换成 class 文件
第三步:绑定
绑定对应的 View
网友评论