ButterKnife是一个专注于Android系统的View注入框架,之前只是简单的使用,知道其用注解的方式帮助减少我们findViewById(),同时在编译期自动生成XXXX_ViewBinding类帮组我们实现findViewById()和绑定回调方法的操作;但是具体的实现细节并不清楚,如何生成的XXXX_ViewBinding类的呢?以8.8.1版本为例;
本篇文章主要分为以下几个步骤
- 注解相关知识;
- 自动生成XXXX_ViewBinding类;
- findViewById()等的具体操作;
注解
我们可以通过这面文章深入理解Java注解类型(@Annotation)了解关于注解的概念和自定义注解步骤;
编译生成XXXX_ViewBinding类
在编译期间,根据ButterKnifter的注解就会生成XXXX_ViewBinding类,是如何实现的呢?
processor是注解(Annotation)处理器的接口;其实现类必须有一个公共的无参构造函数,供工具用来初始化Processor;
1:如果Processor未使用现有对象,则通过构造函数初始化;
2: 调用init()方法;
3: 调用:getSupportedAnnotationTypes():返回支持注解类型的名称,getSupportedOptions和getSupportedSourceVersion()方法;
4: 调用process();
ButterKnifeProcessor类是AbstractProcessor的子类,其实现了processor接口;
主要看下process()方法通过javapoet类创建代码类(即XXXX_ViewBind.java文件);
public final class ButterKnifeProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
//获取使用ButterKnife注解的类;
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
//通过javapoet类创建类xxxx_ViewBinding类,
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
JavaFile javaFile = binding.brewJava(sdk, debuggable, useLegacyTypes);
javaFile.writeTo(filer);
}
return false;
}
}
findAndParseTargets():找到使用ButterKnife注解的类,并一一解析;
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();
//以BindView为例;
// Process each @BindView element.
for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
parseBindView(element, builderMap, erasedTargetNames);
}
Deque<Map.Entry<TypeElement, BindingSet.Builder>> entries =
new ArrayDeque<>(builderMap.entrySet());
//返回bindingMap;主要是为BindingSet中的变量赋值为javapoet生成Java类确定方法、参数;
Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>();
while (!entries.isEmpty()) {
Map.Entry<TypeElement, BindingSet.Builder> entry = entries.removeFirst();
TypeElement type = entry.getKey();
BindingSet.Builder builder = entry.getValue();
TypeElement parentType = findParentType(type, erasedTargetNames);
if (parentType == null) {
bindingMap.put(type, builder.build());
} else {
BindingSet parentBinding = bindingMap.get(parentType);
if (parentBinding != null) {
builder.setParent(parentBinding);
bindingMap.put(type, builder.build());
} else {
// Has a superclass binding but we haven't built it yet. Re-enqueue for later.
entries.addLast(entry);
}
}
}
return bindingMap;
}
BindingSet类的brewJava();主要是根据不同的参数,通过javapoet创建出不同的java类;
JavaFile brewJava(int sdk, boolean debuggable, boolean useLegacyTypes) {
TypeSpec bindingConfiguration = createType(sdk, debuggable, useLegacyTypes);
return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
.addFileComment("Generated code from Butter Knife. Do not modify!")
.build();
}
createType():根据参数,创建不同的方法、变量等;具体可以参考javapoet的用法;
private TypeSpec createType(int sdk, boolean debuggable, boolean useLegacyTypes) {
//创建类名,
TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())
.addModifiers(PUBLIC);//public的
//是否为final修饰
if (isFinal) {
result.addModifiers(FINAL);
}
//创建父类
if (parentBinding != null) {
result.superclass(parentBinding.bindingClassName);
} else {
//没有父类,实现接口
result.addSuperinterface(UNBINDER);
}
//添加成员变量
if (hasTargetField()) {
result.addField(targetTypeName, "target", PRIVATE);
}
//分情况创建构造函数;
if (isView) {
result.addMethod(createBindingConstructorForView(useLegacyTypes));
} else if (isActivity) {
result.addMethod(createBindingConstructorForActivity(useLegacyTypes));
} else if (isDialog) {
result.addMethod(createBindingConstructorForDialog(useLegacyTypes));
}
if (!constructorNeedsView()) {
// Add a delegating constructor with a target type + view signature for reflective use.
result.addMethod(createBindingViewDelegateConstructor(useLegacyTypes));
}
result.addMethod(createBindingConstructor(sdk, debuggable, useLegacyTypes));
//创建unbind()方法
if (hasViewBindings() || parentBinding == null) {
result.addMethod(createBindingUnbindMethod(result, useLegacyTypes));
}
return result.build();
}
编译期间就生成好了XXXX_ViewBinding类了,在app\build\generated\source\apt\debug\包名\ 目录下;
findViewById()等的具体操作
通过startActivity()初始化Activity之后,在onCreate()方法中调用ButterKnife.bind(this),在setContentView()方法之后;
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();//Decor
return createBinding(target, sourceView);
}
createBinding():通过构造函数初始化对应的XXXX_ViewBinding类
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
Class<?> targetClass = target.getClass();
//找到target对应的XXXX_ViewBinding的构造函数;
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//通过构造函数初始化XXXX_ViewBinding类;
return constructor.newInstance(target, source);
}
findBindingConstructorForClass():找到对应的XXX_ViewBinding类构造函数;
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
//BINDINGS是LinkedHashMap,当前对象的cls作为key,对应的ViewBinding类的构造函数作为value;
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null) {
return bindingCtor;
}
String clsName = cls.getName();
//系统的类除外
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
return null;
}
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
//存入LinkedHashMap中;
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
通过构造函数初始化XXXX_ViewBinding类,以BottomNavigationActivity_ViewBinding为例
@UiThread
public BottomNavigationActivity_ViewBinding(BottomNavigationActivity target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
public BottomNavigationActivity_ViewBinding(final BottomNavigationActivity target, View source) {
this.target = target;
View view;
//通过view.findViewById()找到对应的View;
view = Utils.findRequiredView(source, R.id.message, "field 'mTextMessage' and method 'click'");
//强转;
target.mTextMessage = Utils.castView(view, R.id.message, "field 'mTextMessage'", TextView.class);
view2131230856 = view;
//通过@OnClick()绑定点击事件
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.click();
}
});
//通过@BindString()获取string;
Context context = source.getContext();
Resources res = context.getResources();
target.name = res.getString(R.string.app_name);
}
以上就是ButterKnife的主要分析,如有问题,请多指教,谢谢!