DataBinding是一个实现数据和UI绑定的框架,同时也是实现MVVM模式比较常用的一个依赖工具库.简单说把数据映射到 view 的 xml中,不用去findViewById 数据和UI建立映射关系,有点类似bind机制的内存映射,只是这里不是内存的映射而是数据和UI的映射
databinding 并不是MVVM特有的,MVP架构也可以使用,databinding是工具而已
viewModle 依赖库也和MVVM的 VM不是一个东西,VM是控制层 而viewModel是一个jetpack的组件库 mvvm更适合界面更新频繁 数据在多个页面服用的场景
使用
1.开启 DataBinding 功能
哪个module需要使用DataBinding就在对应的build.gradle编译配置文件中添加如下代码声明。开启后,在编译时就会执行Databinding相关的Task.
android {
...
dataBinding {
enabled = true
}
...
}
2.布局中的变化
布局文件的写法变化,在之前的层级上增加了layout 和data标签,data标签不是必须的。
其中myuser是一个临时命名。
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".ThirdActivity">
<data>
<variable
name="myuser"
type="com.android.projects.firstkotlinapplication.databinding.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="@{myuser.name}"/>
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="@{myuser.age}"/>
</LinearLayout>
</layout>
必须要使用 <layout> 这个标签作为 xml 的根部局,所有 DataBinding 的内容都写在 <data >标签内
<import > 是导入所使用的对象的类型
<variable > 是具体声明一个数据对象,<name> 是这个对象的名字,<type> 是这个对象的类型,<variable > 可以有多个
使用 @{ } / @{book.price} 的方式可以把这个声明出来的数据对象的莫个数据设置给我们的 view
3.Activity中的实现
替换之前的setContentView(布局),其实源码里面还是调用了。
binding= DataBindingUtil.setContentView(this,R.layout.activity_main);
user=new User("测试","密码:999");
binding.setUser(user);
binding.setVariable(BR.name,"测试。。。。。");
再看下User类的定义,我们让这个继承BaseObservable ,同时通过注解再对象的get XX方法上,完成定义。
public class User extends BaseObservable {
private String name;
private String pwd;
public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
@Bindable
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
notifyPropertyChanged(BR.pwd);
}
}
这样,整个简单实用,基本就完成了,当然我们实际使用时,数据更新,可能是从网络获取到数据,也就是viewModel更新数据,更新User对象,更新UI。
源码分析
1.布局文件分析
我们可能会想到,布局文件这样写,怎么就能实现解析了。databinding框架在我们编译时候,会生成几个重要的文件如下图
首先是两个布局文件,第一个里面文件中,部分代码如下,我们布局中的每个View都被标记为targets,定义了一个tag,同时view表明之前是一个什么View类型。第二个布局,则是和我们最原始的布局几乎一致,只是每个View 中多了一个tag android:tag="binding_1"
<Targets>
<Target tag="layout/activity_main_0" view="LinearLayout">
<Expressions />
<location endLine="35" endOffset="18" startLine="9" startOffset="4" />
</Target>
除了上面生成的代码,还有一个比较重要的生成类如下:
2.继续看DataBindingUtil类
我们在activity中使用这个工具类去设置布局,里面设置布局,里面可以看到还调用setContentView, 然后获取到布局的content再作为参数传到bindToAddedViews方法
public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
int layoutId, @Nullable DataBindingComponent bindingComponent) {
activity.setContentView(layoutId);
View decorView = activity.getWindow().getDecorView();
ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}
里面调用bind 方法,同样会调用
sMapper.getDataBinder(bindingComponent, roots, layoutId)
3.APT生成的DataBinderMapperImpl类 的getDataBinder方法
看下面代码,如果命中判断,标签是我们定义的最外层的activity_main_0 就创建ActivityMainBindingImpl ,注意activity_main_0 就是我们布局提到的第一个生成的布局文件中的 tag,而创建的实现类,就是APT生成的第三个类
public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
if(localizedLayoutId > 0) {
final Object tag = view.getTag();
if(tag == null) {
throw new RuntimeException("view must have a tag");
}
switch(localizedLayoutId) {
case LAYOUT_ACTIVITYMAIN: {
if ("layout/activity_main_0".equals(tag)) {
return new ActivityMainBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
}
}
}
return null;
}
4.创建ActivityMainBindingImpl类
注意到构造方法里面传入了3,这个3,就是布局文件,原始布局文件中View的个数,比如我一个线性布局中嵌套放了5个TextView,那么这个地方就是6.里面主要是创建 Object[] bindings 数组,存放的是对XML文件信息读取数据,主要调用mapBindings方法,里面代码多,主要就是解析XML保存到数组中。
public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
}
protected static Object[] mapBindings(DataBindingComponent bindingComponent, View[] roots,
int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
Object[] bindings = new Object[numBindings];
for (int i = 0; i < roots.length; i++) {
mapBindings(bindingComponent, roots[i], bindings, includes, viewsWithIds, true);
}
return bindings;
}
//mapBindings 方法太长了。不复制了
>>>>>>>>>>>>> 上面是调用DataBindingUtil设置布局的逻辑
>>>>>>>>>
二、调用 binding.setVariable源码分析
这个方法点进去是一个抽象方法,里面具体实现在APT生成的ActivityMainBindingImpl类中。
1. setVariable方法
我们在更改User对象时候,可能直接调用setUser("密码","123456");就如下的判断,BR 也是生成的一个类,代码中all是0 .如果命中的是user就调用setUser方法
public boolean setVariable(int variableId, @Nullable Object variable) {
boolean variableSet = true;
if (BR.user == variableId) {
setUser((com.example.databindingdemo.User) variable);
}
else {
variableSet = false;
}
return variableSet;
}
public class BR {
public static final int _all = 0;
public static final int name = 1;
public static final int pwd = 2;
public static final int user = 3;
}
核心还是调用下面的updateRegistration 方法
public void setUser(@Nullable com.example.databindingdemo_20210117.User User) {
updateRegistration(0, User);
this.mUser = User;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.user);
super.requestRebind();
}
5.updateRegistration 方法
CREATE_PROPERTY_LISTENER是一个回调接口,onCreate方法中会创建弱引用的监听,里面
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
下面方法中首先从饮用的监听的数组中取出弱引用的监听,如果不为空,调用registerTo方法注册
private boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return unregisterFrom(localFieldId);
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
registerTo(localFieldId, observable, listenerCreator);
return true;
}
if (listener.getTarget() == observable) {
return false;//nothing to do, same object
}
unregisterFrom(localFieldId);
registerTo(localFieldId, observable, listenerCreator);
return true;
}
5.调用registerTo方法
listenerCreator就是之前的那个CREATE_PROPERTY_LISTENER,
调用create方法就是调用 return new WeakPropertyListener(viewDataBinding,
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return;
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
if (mLifecycleOwner != null) {
listener.setLifecycleOwner(mLifecycleOwner);
}
}
listener.setTarget(observable);
}
上面的判断中的代码:listener.setLifecycleOwner(mLifecycleOwner);
里面其实就是把被观察者 与listener绑定,里面就是前面说到的lifecycle的addObserver()
public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
mObservable.setLifecycleOwner(lifecycleOwner);
}
重点还是要看 listener.setTarget(observable);
public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
mObservable.addListener(mTarget);
}
}
再继续看进去 addListener 方法, 是一个接口的抽象方法,里面四个实现,重点看WeakPropertyListener的里面会调用到 target.addOnPropertyChangedCallback(this);
会执行到 下面方法,里面的mCallbacks.add(callback);ViewDataBinding保存在一个List中
public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
synchronized (this) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
}
mCallbacks.add(callback);
}
5.
上面只是完成updateRegistration(0, User);
方法的逻辑实现更新监听,执行Dirty 再执行下面我们看
notifyPropertyChanged(BR.user);
方法实现
public void setUser(@Nullable com.example.databindingdemo.User User) {
updateRegistration(0, User);
this.mUser = User;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.user);
super.requestRebind();
}
public void notifyPropertyChanged(int fieldId) {
synchronized (this) {
if (mCallbacks == null) {
return;
}
}
mCallbacks.notifyCallbacks(this, fieldId, null);
}
三、刷新流程
1.notifyPropertyChanded 方法调用。
重点看notifyRecurse(sender, arg, arg2);
方法,后面都是移除回调
public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
mNotificationLevel++;
notifyRecurse(sender, arg, arg2); //重点
mNotificationLevel--;
if (mNotificationLevel == 0) {
if (mRemainderRemoved != null) {
for (int i = mRemainderRemoved.length - 1; i >= 0; i--) {
final long removedBits = mRemainderRemoved[i];
if (removedBits != 0) {
removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits);
mRemainderRemoved[i] = 0;
}
}
}
if (mFirst64Removed != 0) {
removeRemovedCallbacks(0, mFirst64Removed);
mFirst64Removed = 0;
}
}
2.
private void notifyRecurse(T sender, int arg, A arg2) {
...删除其他代码
notifyRemainder(sender, arg, arg2, remainderIndex)
}
调用notifyFirst64方法
notifyFirst64(sender, arg, arg2);
再调用到
private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex,
final int endIndex, final long bits) {
long bitMask = 1;
for (int i = startIndex; i < endIndex; i++) {
if ((bits & bitMask) == 0) {
mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
}
bitMask <<= 1;
}
}
再调用到 PropertyChangeRegistry 类的onPropertyChanged
方法
执行到监听的属性改变方法
public void onPropertyChanged(Observable sender, int propertyId) {
ViewDataBinding binder = mListener.getBinder();
if (binder == null) {
return;
}
Observable obj = mListener.getTarget();
if (obj != sender) {
return; // notification from the wrong object?
}
binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId); //重点
}
再调用 binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
再调用requestRebind
下面看编舞者类的另一个分支,通过一个handler执行runnable。
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
final LifecycleOwner owner = this.mLifecycleOwner;
if (owner != null) {
Lifecycle.State state = owner.getLifecycle().getCurrentState();
if (!state.isAtLeast(Lifecycle.State.STARTED)) {
return; // wait until lifecycle owner is started
}
}
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
if (USE_CHOREOGRAPHER) {
mChoreographer.postFrameCallback(mFrameCallback);
} else {
mUIThreadHandler.post(mRebindRunnable); //重点
}
}
}
Runnble中执行 executePendingBindings()
private final Runnable mRebindRunnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
mPendingRebind = false;
}
processReferenceQueue();
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
// Nested so that we don't get a lint warning in IntelliJ
if (!mRoot.isAttachedToWindow()) {
// Don't execute the pending bindings until the View
// is attached again.
mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
return;
}
}
executePendingBindings();
}
};
上面代码会再次执行到ViewDataBinding类的executeBindings方法,
会调用到APT 生成类的executeBindings方法
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String userName = null;
com.example.databindingdemo.User user = mUser;
java.lang.String userPwd = null;
if ((dirtyFlags & 0xfL) != 0) {
if ((dirtyFlags & 0xbL) != 0) {
if (user != null) {
// read user.name
userName = user.getName();
}
}
if ((dirtyFlags & 0xdL) != 0) {
if (user != null) {
// read user.pwd
userPwd = user.getPwd();
}
}
}
// batch finished
if ((dirtyFlags & 0xbL) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv1, userName);
}
if ((dirtyFlags & 0xdL) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv2, userPwd);
}
}
可以看到上面的代码,TextViewBindingAdapter.setText ,感觉快到了!
public static void setText(TextView view, CharSequence text) {
final CharSequence oldText = view.getText();
if (text == oldText || (text == null && oldText.length() == 0)) {
return;
}
if (text instanceof Spanned) {
if (text.equals(oldText)) {
return; // No change in the spans, so don't set anything.
}
} else if (!haveContentsChanged(text, oldText)) {
return; // No content changes, so don't set anything.
}
view.setText(text);
}
上面就是当我们调用setUser这种或者setVariable方法,会调用到notifyPropertiChange方法,去更新binding,因为我们在使用DataBindingUtil 设置布局时,建立了监听关系,这个时候会通知发生变化,当然,中间还增加判断了,被观察者是否处于active状态,才能更新,最后会调用到我们在APT阶段生成的类的executeBindings方法, 里面还是调用了对应tag的更新UI的方法。最终其实就是跟我们使用MVP 加回调方式实现是一样的,只是框架对这套回调,封装在框架里面。防止开发者忘记注销,增加是否激活状态等封装,使用更方便。
小结
首先就是activity是一个被观察者,被databinding观察他的生命周期变化。只有当处于激活状态,才会回调更新UI;
其实activity是观察者,每个activity 可能会用到多个Viewmodel 对象,那么activity会在APT生成的实现类中创建数组,存放对应的tag,当数据tag发生变化后,通过DataBinding框架的监听,来通知activity 刷新UI。
最后我们会发现,这个框架其实思路还是比较简单,只是对绑定关系,回调,即是否处于active状态等逻辑封装判断,不用我们手写代码。使用起来简单,高效。