浅析butterknife之页面控件绑定流程及源码分析
2019-04-14 本文已影响10人
zl_adams
我们平时在activity使用butterknife时,会有以下类似代码
(注解@BindView和 @OnClick都是用于生成MainActivity_ViewBinding内容的)
public class MainActivity extends AppCompatActivity {
@BindView(R.id.btn)
Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@OnClick(R.id.btn)
public void onViewClicked() {
Toast.makeText(this, "点击了按钮", Toast.LENGTH_SHORT).show();
}
}
查看ButterKnife类查看bind(this)方法
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
//内部经过LayoutInflater.from(context).inflate(R.layout.activity_main, viewGroup)得到的view
View sourceView = target.getWindow().getDecorView();
return createBinding(target, sourceView);
}
通过反射获取ViewBinding:MainActivity_ViewBinding,执行页面控件的绑定
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());
//这里获取到的是页面MainActivity对应的ViewBinding:MainActivity_ViewBinding(编译期生产的文件)的Constructor
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
//执行MainActivity_ViewBinding的构造方法,实现页面控件的绑定
return constructor.newInstance(target, source);
}
//省略一些非重点代码
...
}
查看findBindingConstructorForClass(Class<?> cls)如何返回MainActivity_ViewBinding的构造器方法Constructor
@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 {
//重点在这里,通过MainActivity和_ViewBinding拼接,获取MainActivity_ViewBinding
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;
}
真正实现控件绑定是在MainActivity_ViewBinding进行的
@UiThread
public MainActivity_ViewBinding(final MainActivity target, View source) {
this.target = target;
View view;
//找到对应的控件
view = Utils.findRequiredView(source, R.id.btn, "field 'btn' and method 'onViewClicked'");
//转型
target.btn = Utils.castView(view, R.id.btn, "field 'btn'", Button.class);
view2131165218 = view;
//设置点击监听
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.onViewClicked();
}
});
}
总结整个流程:
1、onCreated方法中执行ButterKnife.bind(this);
2、获取setContentView(int layoutId)对应的View;
3、根据bind()方法传递的Activity,通过字符串拼接“ clsName + "_ViewBinding" ”,获取到MainActivity_ViewBinding,进而获取到MainActivity_ViewBinding的Constructor;
4、传入第二部获取到的View,执行MainActivity_ViewBinding的Constructor方法,创建MainActivity_ViewBinding的实例对象,绑定页面的控件变量和对应的控件。