天天看点

Databinding源码如何实现数据绑定更新?看这篇就够了

DataBinding是什么这个不用多说,大部分同学使用了很久。对于我们来说有很多好处,提高了代码的简洁度,让代码的耦合度降低等等。那么,DataBinding到底如何就实现了数据的绑定,我们光写一个布局,怎么就能实现数据的更新了?本文从BaseObservable开始分析一下DataBinding的一些原理。

目录

1.DataBinding中的BaseObservable类

2.Databinding实现源码分析

1.inflate方法:

2.生成类绑定ViewModel

3.设置可观察数据监听的过程

3.总结

首先了解一下DataBinding中使用的BaseObsevable

1.DataBinding中的BaseObservable类

BaseObservable类是Databindbing提供的一个拥有观察能力的类,通过继承它我们能够实现数据改变的时候动态的通知。

1.基础的实现类使用

BaseObservable继承自Observable,同时提供了一些基础的数据类型的实现类:

ObservableBoolean

ObservableChar

ObservableInt

ObservableFloat
           

这些基础类,我们在一个ViewModel中声明这些类的对象,在数据发生改变的时候调用对应的set方法更新数据,绑定的View就能自定更新了。

2.通过继承BaseObservable自定义一个可观察的类。

对于基本数据类不够用的时候,我们可以自定义一个类,在类中使用@Bindable来注解这些字段,同时提供对应的set方法,在数据变化的时候调用在set方法中调用notifyChange或者notifyPropertyChanged()方法通知数据改变。

Databinding源码如何实现数据绑定更新?看这篇就够了

如上申明后,我们可以在绑定的View中引入这个类的对象,把类对象在布局中帮到View,在数据变化的时候就会自动更新。

假设我们定义了一个Product类型的对象如下:

Databinding源码如何实现数据绑定更新?看这篇就够了

假设请求网络后更新数据如下:

Databinding源码如何实现数据绑定更新?看这篇就够了

布局中使用如下:

Databinding源码如何实现数据绑定更新?看这篇就够了

这样就完成了一个可观察类型的定义和绑定。

[email protected]注解干了什么

通过一个注解最后就能完成可观察数据的创建,到底干了些什么?

我们根据官方文档知道,在@Bindable注解后,调用notifyPropertyChanged方法的时候要传递一个「BR.属性」如:BR.productName

Databinding源码如何实现数据绑定更新?看这篇就够了

也就是说被@Bindable注解就会在BR文件中生成对应的值。但是上面实例中我们没有注解loginViewModl、userInfo、product这几个字段,只是在布局中绑定对象使用了,BR文件也生成了一个字段,这是为什么?

我们查询每个布局生成的对应的一个java类,比如我们目前的布局是activity_login.xml则对应的是ActivityLoginBinding。我们看到,果然在这个类中自定生成的代码中加上了布局中定义的variable中的对象注解。(也就是在布局中添加的variable节点都会被自动使用@Bindable注解生成BR对应的id)

Databinding源码如何实现数据绑定更新?看这篇就够了

查看这个生成类的父类,它继承自BaseObservable,这下就真相大白了。

Databinding源码如何实现数据绑定更新?看这篇就够了

原来databinding会帮我们把布局中申明为variable中的对象添加上@Bindable注解,这样,这些对象也拥有了可观察的能力,当数据变化的时候就会自动更新。其中ActivityLoginBinding有一个子类来实现其他的一些功能:

Databinding源码如何实现数据绑定更新?看这篇就够了

Activity代码中绑定

Databinding源码如何实现数据绑定更新?看这篇就够了

生成的类中设置绑定的逻辑

Databinding源码如何实现数据绑定更新?看这篇就够了

2.Databinding实现源码分析

Databinding是如何实现绑定的?我们在布局中添加了layout层,然后再layout中添加了variable节点,在节点中添加引用的ViewModel等。同时添加了其他的View布局,最后通过编译生成一个布局名称+Binding的类比如布局是activity_login.xml,则生成ActivityLoginBinding.java,在Activity的setContentView中通过inflate来加载布局,创建这个生成的Binding对象。

Databinding源码如何实现数据绑定更新?看这篇就够了

因此从此处开始分析:

在build生成的文件中找到如ActivityLoginBinding的生成类。

1.Databinding的inflate方法

Databinding源码如何实现数据绑定更新?看这篇就够了

能看到,在代码中加载了布局:

Databinding源码如何实现数据绑定更新?看这篇就够了
Databinding源码如何实现数据绑定更新?看这篇就够了

在DataBindingUtil类中继续调用inflate,在这里通过View的inflate解析布局生成View对象:

Databinding源码如何实现数据绑定更新?看这篇就够了

最后调用到了bind方法中,把生成的View传递进去:

Databinding源码如何实现数据绑定更新?看这篇就够了

来到了sMapper:

最后在DataBinderMapperImpl中找到getDataBinder方法:

Databinding源码如何实现数据绑定更新?看这篇就够了

查看构造方法:

Databinding源码如何实现数据绑定更新?看这篇就够了
Databinding源码如何实现数据绑定更新?看这篇就够了

mapBinding()方法创建了一个Object[]集合,最后从mapBindings方法中看到

Databinding源码如何实现数据绑定更新?看这篇就够了
Databinding源码如何实现数据绑定更新?看这篇就够了
Databinding源码如何实现数据绑定更新?看这篇就够了

最后解析完成返回了bindings[]集合,保存了布局中的View。

前面看到了在构造方法中调用super,并取出了集合中保存的View传递给父类的构造方法,查看父类的构造方法:

Databinding源码如何实现数据绑定更新?看这篇就够了

这样就解析到了布局中设置了id的View,并且保存到了生成的ActivityLoginBinding类中。

我们在代码中创建了对应布局的Binding对象后,就可以通过对象获取到布局中的View来操作。

2.生成类绑定ViewModel

我们在绑定布局的时候需要把当前的ViewModel绑定到布局生成的Binding类中。如下:

Databinding源码如何实现数据绑定更新?看这篇就够了

这个生成的setLoginViewModel方法就是在布局中设置了variable的一个对象。我们在生成的Binding的实现类找到了setLoginViewModel的实现类

Databinding源码如何实现数据绑定更新?看这篇就够了
Databinding源码如何实现数据绑定更新?看这篇就够了

notifyPropertyChanged方法如下:

Databinding源码如何实现数据绑定更新?看这篇就够了

mCallbacks是一个PropertyChangeRegistry类型的对象

Databinding源码如何实现数据绑定更新?看这篇就够了

最终会来到CallbackRegistry类,调用notifyCallbacks方法

Databinding源码如何实现数据绑定更新?看这篇就够了

可以看到,还调用了notifyRecurse(sender, arg, arg2);

最后来到了notifyCallbacks方法:

Databinding源码如何实现数据绑定更新?看这篇就够了

然后循环调用了mNotifier的onNotifyCllback。

这个mNotifier在其构造方法中传递来的:

Databinding源码如何实现数据绑定更新?看这篇就够了

也就是前面我查看的PropertyChangeRegistry类中传递来的。

Databinding源码如何实现数据绑定更新?看这篇就够了

也就是最后循环调用了CallbackRegistry.NotifierCallback的onNotifyCallback方法,它接收了一个OnPropertyChangedCallback参数,调用其onPropertyChanged方法。

(最后我们会知道这个onPropertyChanged是谁的方法)

到这里就调用结束了,我们现在要找一下PropertyChangeRegistry这个类被谁使用了,干了什么。回到之前的setLoginViewModel()绑定ViewModel的时候。

3.设置可观察数据监听的过程

Databinding源码如何实现数据绑定更新?看这篇就够了

这里调用了一个super.requestRebind();方法

Databinding源码如何实现数据绑定更新?看这篇就够了

这个方法中做了一个判断,最终执行到了Callback。不管如何执行,最后都来到了runable中:

Databinding源码如何实现数据绑定更新?看这篇就够了

很明显能看到最后的一句executePendingBindings应该是核心代码:

Databinding源码如何实现数据绑定更新?看这篇就够了
Databinding源码如何实现数据绑定更新?看这篇就够了

其中mRebindCallbacks是CallbackRegistry对象,前面我们已经见过了,最后会循环回调。

执行到了一句核心executeBindings(),这是一个抽象类,去对应的Activity的生成的实现类中找一下实现方法:

Databinding源码如何实现数据绑定更新?看这篇就够了
Databinding源码如何实现数据绑定更新?看这篇就够了

这里我们看到获取了ViewModel中保存的对象,然后调用了一个updateLiveDataRegistration()方法,因为DataBinding中可以使用LiveData这个可以保存数据的库,我们在这里刚好也使用到了。

中间还有一些对布局中设置的数据的处理,比如判断,拼接等,最后我们看到调用了setText等方法:

Databinding源码如何实现数据绑定更新?看这篇就够了

这里注意到一点,并没有直接调用TextView的setText方法,而是使用了

androidx.databinding.adapters.TextViewBindingAdapter 

com.lll.digua.bindingadpter.BaseViewBindingAdpterKt
           
Databinding源码如何实现数据绑定更新?看这篇就够了
Databinding源码如何实现数据绑定更新?看这篇就够了

查看这两个类发现,原来DataBinding底层已经设置了很多的BindingAdapter,对应了不同的View,最后调用BindingAdapter的方法设置数据。

到这里我们还有一个方法没有看,就是updateLiveDataRegistration(2, loginViewModlIsShow);、updateRegistration(3, loginViewModlOldUserInfo);这里又干了什么?从代码看到,应该是一个View对应一个updateXXX方法第一个参数localFieldId,是对应的一个数字,第二个参数是要使用的数据对象比如getProduct。

Databinding源码如何实现数据绑定更新?看这篇就够了

内部调用了updateRegistration()这个方法,第一个参数是localFieldId,第二个参数是一个observable,也就是我们传递的getProduct,刚好我们的Prduct就是继承自BaseObservable

Databinding源码如何实现数据绑定更新?看这篇就够了
Databinding源码如何实现数据绑定更新?看这篇就够了

第三个参数是一个CREATE_PROPERTY_LISTENER,其实是CreateWeakListener对象。

Databinding源码如何实现数据绑定更新?看这篇就够了

这个create方法返回的类WeakPropertyListener继承了OnPropertyChangedCallback,实现了一个ObservableReference接口。其中有几个方法addListener、onPropertyChanged注意后边会用到这里的类和方法.

Databinding源码如何实现数据绑定更新?看这篇就够了

updateRegistration方法内部:

Databinding源码如何实现数据绑定更新?看这篇就够了
Databinding源码如何实现数据绑定更新?看这篇就够了

能看到它是WeakListener类型的数组。在ViewDataBinding被初始化,也就是DataBinding生成的类初始化的时候创建。查看生成类的代码我们知道,这里的root就是每个布局生成的View,localFieldCount就是布局中药绑定的由id的View的个数,比如我们代码中是5个View就是5.

这里的WeakListener代码如下:

内部有一个setTarget方法,给observable对象绑定了listener。

Databinding源码如何实现数据绑定更新?看这篇就够了

回到updateRegistration方法,刚开始从mLocalFieldObservers取值listener是null。所以会执行到判断里边。执行registerTo(localFieldId, observable, listenerCreator);

Databinding源码如何实现数据绑定更新?看这篇就够了
Databinding源码如何实现数据绑定更新?看这篇就够了

在方法里边通过listenerCreator调用create方法获取一个listener。

也就是调用前面的CreateWeakListener的create方法创建了一个WeakPropertyListener类对象,它是一个Listener,

把它保存到mLocalFieldObservers中。

最后一句调用了listener.setTarget(observable),就是前面说的WeakListener中的setTarget方法

Databinding源码如何实现数据绑定更新?看这篇就够了

这里的mObsevable对象就是WeakListener的第三个参数,而这个参数this是一个ObservableReference类型,也就是WeakPropertyListener类本身。

Databinding源码如何实现数据绑定更新?看这篇就够了

所以上面的setTarget方法也就是中的mObservable.addListener也就是WeakPropertyListener的方法了。

Databinding源码如何实现数据绑定更新?看这篇就够了

addListener的参数target就是我们传递进来的继承了BaseObservable的对象。

最后调用了target.addOnPropertyChangedCallback(this);这里的this指的就是

OnPropertyChangedCallback这个实现,它有一个方法onPropertyChanged:

Databinding源码如何实现数据绑定更新?看这篇就够了

这里的onPropertyChanged什么时候会被调用呢?

前面我们在分析notifyPropertyChanged()(一个字段更新数据后要调用它来通知更新)。

最后说了一个PropertyChangeRegistry类,它参数有一个CallbackRegistry.NotifierCallback对象,它的onNotifyCallback方法接收OnPropertyChangedCallback类型的参数,最后调用onPropertyChanged方法。

到这里就接上了前面的内容,整个流程走通。

3.总结

从整个分析可以看出:

1.通过inflate的调用解析布局,生成View保存在自动生成的类中提供给我们使用。

2.在创建了Binding对象后绑定ViewModel时调用setxxxViewModel方法时调用了requestRebind()方法,这个方法内部通过Handler执行了一个runable,最后执行到生成类的executeBindings()方法中.在这个方法中对布局中绑定数据进行操作,然后执行了updateRegistration和updateLiveDataRegistration相关的方法。内部又创建了实现了ObservableReference接口的WeakPropertyListener对象,来处理回调,添加可观察对象的监听以供数据改变时调用。

3.每次在需要更新时调用notifyPropertyChanged()方法,然后调用PropertyChangeRegistry类对象的notifyCallbacks方法,最后调用到onPropertyChanged,通知更新。更新的操作在生成类的executeBindings方法中完成。

以上为个人的学习笔记,由于水平有限,作文仓促,如有发现问题请提出意见,谢谢~