前言
在没有使用DataBinding之前,我的项目都是使用ButterKnife,当然对于ButterKnife大家估计都熟悉的不要太熟悉了。本文我也就当自己的一个总结。
基本使用
基本使用我就直接贴相关的ButterKnife使用教程的文章链接:
从Github链接我们也可以看出有哪些基本的功能注解:
butterknife : android library model 提供android使用的API
butterknife-annotations: java-model,使用时的注解
butterknife-compiler: java-model,编译时用到的注解的处理器
butterknife-gradle-plugin: 自定义的gradle插件,辅助生成有关代码
butterknife-integration-test: 该项目的测试用例
butterknife-lint: 该项目的lint检查
sample: demo
我们同时可以在butterknife-annotations里面看到基本的注解:
具体流程
我们从二个方面来看:
1.生成XXXX_ViewBinding.java文件:
public class AAA extends Activity{
@BindView(R.id.textView)
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.aaa);
}
}
我们写一个最简单的Demo,就绑定了一个TextView。(这时候并没有ButterKnife.bind(this)代码) 我们按下Android Studio的编译按钮,当编译完成后,我们查看文件:
我们可以发现在编译后多了一个AAA_ViewBinding.java文件
public class AAA_ViewBinding implements Unbinder {
private AAA target;//可以看到这里的target是AAA这个Activity的实例对象。
@UiThread
public AAA_ViewBinding(AAA target) {
//先调用这个一个参数的构造函数,然后调用下面的二个参数的构造函数
this(target, target.getWindow().getDecorView());
}
@UiThread
public AAA_ViewBinding(AAA target, View source) {
//这里的target就是AAA这个Activity的实例,
//source就是上面的单个构造函数生成的AAA这个Activity的DecorView
/*(ps:一般我们在Activity中setContentView(R.layout.XXX);
就是在我们写的布局设置到了DecorView里面。)
*/
this.target = target;
//target.tv就是我们写的AAA里面定义的TextView tv;
//通过Utils.findRequiredViewAsType进行tv的实例化,
/*(ps:因为这里是要taget.tv,所以如果我们在AAA里面的TextView用的private就会有问题了
当然实际private的判断是在生成AAA_ViewBinding.java这个文件的时候就会去判断
)*/
target.tv = Utils.findRequiredViewAsType(source, R.id.textView, "field 'tv'", TextView.class);
}
@Override
@CallSuper
public void unbind() {
AAA target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.tv = null;
}
}
我们可以具体看下Utils.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);
}
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.");
}
可以看到最后是View view = source.findViewById(id);
来帮我们TextView赋值。
可能大家会说。那这个AAA_ViewBinding.java文件到底是怎么生成的,为什么编译下后,会生成这么个文件,为什么这个文件里面相关代码也会有等。(初步可以这么理解,就是遍历了我们在AAA.java中写的注解,然后按照相应模板生成一个java文件。)
详细的生成AAA_ViewBinding.java文件可以看以下链接:
2.实例化XXX_ViewBinding.java:
我们在上面的例子中可以看到,在生成的AAA_ViewBinding.java
中有了对我们的TextView
进行赋值。
那么现在的问题就是变成了实例化
AAA_ViewBinding.java
,然后运行相应的这个TextView
赋值的相关代码.
这时候就需要执行ButterKnife.bind(this);(ps:因为我们这个例子是在Activity中)
。我们来看ButterKnife.bind(this)
到底执行了什么:
ButterKnife -- > bind:
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
//target是我们传入的this,也就是AAA这个Activity的实例
//获取到AAA的DecorView实例化对象sourceView
View sourceView = target.getWindow().getDecorView();
//调用createBinding方法
return createBinding(target, sourceView);
}
createBinding:
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
//1.获取AAA的Class
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
//2.通过findBindingConstructorForClass获得构造器
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
//3. 通过构造器实例化
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);
}
}
我们发现主要是第二步,获取构造器,所以我们来看下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 {
//加载了(clsName + "_ViewBinding")的类,也就是AAA_ViewBinding的Class对象,
//然后在用这个AAA_ViewBinding的Class对象获取构造器。
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;
}
所以当执行了ButterKnife最后就是通过构造函数获取了AAA_ViewBinding
的实例,而在AAA_ViewBinding.java
的构造函数中对TextView
进行了赋值。
结尾
如果哪里有问题,欢迎大家留言提出。我可以及时改正,(▽)