知道了 DataBinding
如何使用之后,就很好奇,这个DataBinding 机制是如何实现的了。但是在 Android Studio
里面怎么也没有找到 ActivityMainBinding
的代码,在 ObservableUser
的 getLastName
方法中设置断点,查看调用栈。我们可以发现,其实是 ActivityMainBinding.executeBindings
方法调用了 model 的 get 方法。
然而,悲剧的是,我想点击查看 executeBindings
调用情况,Android Studio
是跳到了 activity_main.xml
里去了。
好吧,还是想看源码,那就用 dex2jar
和 jd-gui
工具查看了 class.dex 文件,果然看到了源码
当然,其他 Android Studio
上没能看到的代码,如 DataBinderMapper
等的代码也都能找到了。
直接查看 DataBindingUtil.setContentView
里面的源码吧:
MainActivity.onCreate
ActivityMainBinding binding =
DataBindingUtil.setContentView(this, R.layout.activity_main);
省略部分代码,直接来到 DataBindingUtil.bind
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent,
View root, int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}
其中,bindingComponent
为 null
;root
和 layoutId
都对应 activity_main.xml
中定义的 LinearLayout
DataBinderMapper.getDataBinder
public ViewDataBinding getDataBinder(DataBindingComponent paramDataBindingComponent,
View paramView, int paramInt) {
switch (paramInt) {
default:
return null;
case 2130968601:
}
return ActivityMainBinding.bind(paramView, paramDataBindingComponent);
}
ActivityMainBinding.bind
public static ActivityMainBinding bind(View paramView, DataBindingComponent paramDataBindingComponent) {
if (!"layout/activity_main_0".equals(paramView.getTag()))
throw new RuntimeException("view tag isn't correct on view:" + paramView.getTag());
return new ActivityMainBinding(paramDataBindingComponent, paramView);
}
这里可以看到,返回的
ActivityMainBinding
对象是在这里被创建的了
ActivityMainBinding 构造函数
public ActivityMainBinding(DataBindingComponent paramDataBindingComponent, View paramView) {
super(paramDataBindingComponent, paramView, 1);
paramDataBindingComponent = mapBindings(paramDataBindingComponent, paramView, 2, sIncludes, sViewsWithIds);
this.mboundView0 = ((LinearLayout)paramDataBindingComponent[0]);
this.mboundView0.setTag(null);
this.mboundView1 = ((Button)paramDataBindingComponent[1]);
this.mboundView1.setTag(null);
setRootTag(paramView);
invalidateAll();
}
这里还有好多疑问,mboundView0 和 mboundView1 分别对应什么控件呢?setRootTag 和 invalidateAll 都是干啥的呢?
ViewDataBinding.mapBindings
protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
Object[] bindings = new Object[numBindings];
mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
return bindings;
}
private static void mapBindings(DataBindingComponent bindingComponent, View view,
Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
boolean isRoot) {
......
if (view instanceof ViewGroup) {
......
for (int i = 0; i < count; i++) {
......
if (bindings[index] == null) {
bindings[index] = view;
}
......
{
bindings[index] = DataBindingUtil.bind(bindingComponent, included,
layoutId);
}
......
if (!isInclude) {
mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
}
}
}
}
注:这里会递归的执行
mapBindings
将传入的bindings
数据给填充好。binding
数组里面的数据,可能是 view 也可能是 ViewDataBinding 在当期的示例程序中,bindings[0]
是LinearLayout
,bindings[1]
是Button
;所以,ActivityMainBinding.mboundView0
就是 layout 中定义的 LinearLayout;ActivityMainBinding.mboundView1
就是 layout 中定义的 Button。
ViewDataBinding.setRootTag
protected void setRootTag(View view) {
......
view.setTag(R.id.dataBinding, this);
......
}
将 ActivityMainBinding
和布局文件中的 LinearLayout
关联起来了。
ActivityMainBinding 构造函数
public ActivityMainBinding(DataBindingComponent paramDataBindingComponent, View paramView) {
......
invalidateAll();
}
跳过部分代码,调用到 ActivityMainBinding.requestRebind
protected void requestRebind() {
......
if (SDK_INT >= 16) {
mChoreographer.postFrameCallback(mFrameCallback);
} else {
mUIThreadHandler.post(mRebindRunnable);
}
}
假设当前 SDK_INT == 23,直接查看 mFrameCallback
的定义。则在下一帧的时候,调用 mRebindRunnable.run();
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
mRebindRunnable.run();
}
};
最终执行 ActivityMainBinding.executeBindings
方法
protected void executeBindings()
{
......
Handler localHandler = this.mHandler;
ObservableUser localObservableUser = this.mUser;
......
while (true)
{
......
localObject1 = new OnClickListenerImpl();
this.mAndroidViewViewOnCl = ((OnClickListenerImpl)localObject1);
localObject1 = ((OnClickListenerImpl)localObject1).setValue(localHandler);
localObject3 = localObject4;
......
localObject3 = localObservableUser.getLastName();
TextViewBindingAdapter.setText(this.mboundView1, (CharSequence)localObject3);
this.mboundView1.setOnClickListener((View.OnClickListener)localObject1);
return;
......
}
}
注:这里
mUser
和mHandler
是 MainActivity.onCreate 中的设置的:
binding.setUser(user);
binding.setHandler(handler);
这里清楚的看到调用
localObservableUser.getLastName
获取 model 中的数据,然后设置给mboundView1
(Button)新建
OnClickListenerImpl
对象,处理mboundView1
(Button)的点击事件,而最终也还是会调用到Handler.onClickButton
方法上
public static class OnClickListenerImpl implements View.OnClickListener {
private Handler value;
public void onClick(View paramView) {
this.value.onClickButton(paramView);
}
......
}
查看下代码,set 函数中,需要添加一句 notifyPropertyChanged
方法。其实这里对 lastName
的监听者,就是 ViewDataBinding$WeakPropertyListener
,而内部调用的还是 AcitivityMainBinding.handleFieldChange
方法,最终还是调用了 AcitivityMainBinding.requestRebind
。这里就已经和前面分析的过程一样,也就是说最终视图发生改变生效,走的还是消息队列。
public class ObservableUser extends BaseObservable {
......
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(com.netease.mvvmsample.BR.lastName);
}
}
private static class WeakPropertyListener
extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
final WeakListener<Observable> mListener;
@Override
public void onPropertyChanged(Observable sender, int propertyId) {
ViewDataBinding binder = mListener.getBinder();
......
binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}
}
由上面的源码解析,已经知道几点
数据如何和 view 关联起来的
事件处理如何和 view 关联起来的
数据和事件处理的关联发生是扔给消息队列处理的
当数据改变,通知视图改变时,走的是消息队列。因此一次数据改动,并界面可能不会立马生效
数据和视图的绑定,其实是单向的,即数据发生改变通知了视图,而视图发生并不能自动通知数据
虽然没看到 Android Studio 是如何实现代码生成,但相关的工具大家可以看下 javapoet
有了 DataBinding,后面就有人玩出了 MVVM 模式了。当然啦,这里主要是抱着学习的态度在阐述 Android 里面的 DataBinding
,并不是在推崇 DataBinding
或 MVVM
。这些概念有人推崇有人贬低,引用别人的一句话,希望大家对新知识都能做到:
我们需要保持的是一个拥抱变化的心,以及理性分析的态度。
在新技术的面前,不盲从,也不守旧,一切的决策都应该建立在认真分析的基础上,这样才能应对技术的变化
本文来自网易实践者社区,经作者张云龙授权发布。