其实当我们每次点击运行的时候, 都会去扫描运行时的注解,然后自动生成这么一个类,是自动生成的不是我们自己写的。
xxx_ViewBinding 作为类名,实现 Unbinder 在 xxx_ViewBinding 的构造函数里面去 findViewById 或者 setOnclickListener。我们只要在 MainActivity 中去实例化一个 MainActivity_ViewBinding 对象,那么我们 MainActivity 里面的所有属性就都会被赋值了。
ButterKnife.bind(this);
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return createBinding(target, sourceView);
}
target.getWindow().getDecorView();语句获取activity的根视图decorView。读过view源码的同学知道,activity的根view为叫mDecor的Framlayout,也就是这里获取到的sourceView。
获取到sourceView后,将target(就是XXXActivity对象)与sourceView(XXXActivity的根视图)作为参数createBinding。
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
return constructor.newInstance(target, source);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InstantiationException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException("Unable to create binding instance.", cause);
}
}
这里只关心:
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
通过XXXActivity.class获取对应的构造器,看到这里我们可以猜到是通过反射创建Unbinder实例。
果然紧接着就是
return constructor.newInstance(target, source);
先看findBindingConstructorForClass方法:
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
try {
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
}
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
这个方法的作用就是利用XXXActivity.class的类名,找到名叫XXXActivity_ViewBinding的类,并创建一个实例。
这个XXXActivity_ViewBinding类是自动生成的,实现implements Unbinder接口
例如:
public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
@UiThread
public MainActivity_ViewBinding(MainActivity target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
public MainActivity_ViewBinding(MainActivity target, View source) {
this.target = target;
//这里因为xml布局只有一个:TextView:id:tv_content,变量名:tvContent,如果有多个,则创建多个
target.tvContent = Utils.findRequiredViewAsType(source, R.id.tv_content, "field 'tvContent'", TextView.class);
}
@Override
@CallSuper
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.tvContent = null;
}
}
这个类有两个构造方法,和一个unbind函数。我们先看构造方法。不知道大家还记得么,构造方法的两个参数:target(xxxActivity) , source (xxxActivity的根视图)。 构造方法只有一个方法findRequiredViewAsType,我们看下这个方法做了什么:
public static <T> T findRequiredViewAsType(View source, @IdRes int id, String who,
Class<T> cls) {
View view = findRequiredView(source, id, who);
return castView(view, id, who, cls);
}
先看findRequiredView:
public static View findRequiredView(View source, @IdRes int id, String who) {
//终于找到了....
View view = source.findViewById(id);
if (view != null) {
return view;
}
String name = getResourceEntryName(source, id);
throw new IllegalStateException("Required view '"
+ name
+ "' with ID "
+ id
+ " for "
+ who
+ " was not found. If this view is optional add '@Nullable' (fields) or '@Optional'"
+ " (methods) annotation.");
}
终于找到了findViewById。。。。可以看到,函数的返回值是View,我们需要的是TextView,所以第二个函数castView出场了,这个函数的作用就是类型强制转换。
到这里其实就差不多了。。。
谢谢阅读,我这里也是学习的态度在这里分享,有什么问题希望大家能提出来能提出来,随时可以和我交流探讨:QQ:707086125 微信:loveme_dp