databinding解决什么问题
传统数据的更新方式是命令式的,需要开发者主动获取某个ui控件,并触发更新.如下所示
...
User user = loadUserFromNet();
TextView userName = findViewById(R.id.name);
userName.setText(user.name);
这样做主要的缺陷包括:
- 当User对象发生变化时,需要开发者索引到所有的需要更新的UI并主动刷新
- 获取控件可能出现空指针
- 需要维护modle与UI多对多的关系.
databinding组件解决数据驱动ui更新问题,并将数据与UI的关联关系封装在框架内,简化开发者的工作.
databinding用法
- 在build.gradle中申明使用databinding组件
- 定义viewModel对象(User),需要继承自androidx.databinding.BaseObservable
- 在需要观察ViewModel的xml中引入User,并通过@{user.xxx}来绑定对应的属性
- 在Activity中通过DataBindingUtil建立xml和ViewModel的关联关系
//app下的build.gradle
android {
defaultConfig {
//使用databinding
dataBinding{
enabled true
}
}
}
//定义viewModel
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);
// notifyPropertyChanged(BR.user);
}
@Bindable
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
notifyPropertyChanged(BR.pwd);
}
}
//mainactivity.xml文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data><!--这里面就用来定义数据源-->
<variable
name="user"
type= "com.example.databindingdemo.User"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:textSize="50sp"
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:textSize="50sp"
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.pwd}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
</layout>
//mainActivity
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
//从model层来的数据
User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//这一行就是完成XML布局的初始化
binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
//数据是从网络或是数据库拿来的
user=new User("jett","123");
binding.setUser(user);//view.setText(text);
}
}
dataBinding原理
databinding框架会通过apt默认生成一些文件,如下图所示
- ActivityMainBindingImpl是ViewDataBinding的具体实现类,它的作用在于将需要监听数据变化的view集合起来,当viewmodel发生变化时,通知view做修改.
- BR.java定义了view中使用到的viewmodel中的属性的信息
- DataBinderMapperImpl.java维护了BR.java中的key和index的映射关系.
//BR.java
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;
}
xml初始化与解析
- 将定义的xml中的每个view绑定一个tag,tag的名字为binding_index,这个index在BR.java中自动定义.
<TextView
android:textSize="50sp"
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:textSize="50sp"
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="binding_1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
建立tag与view的绑定关系数组
- 将实际的view添加到decorView中用于显示
- 将tag与view绑定关系组合到数组中
- DataBindingUtil.setContentView返回的是ActivityMainBindingImpl
binding = DataBindingUtil.setContentView(this,R.layout.activity_main)
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);
}
private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 1
, (android.widget.TextView) bindings[1]
, (android.widget.TextView) bindings[2]
);
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
this.tv1.setTag(null);
this.tv2.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
注册监听器
当setUser时,注册监听器,0代表的是BR.java中的_all,初始情况下,所有属性的监听器都为空,通过CreateWeakListener构造WeakPropertyListener,并将自己注册到监听器列表中
binding.setUser(user);//view.setText(text);
public void setUser(@Nullable com.example.databindingdemo.User User) {
updateRegistration(0, User);
this.mUser = User;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.user);
super.requestRebind();
}
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;
}
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);
}
private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
final WeakListener<Observable> mListener;
public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
mListener = new WeakListener<Observable>(binder, localFieldId, this);
}
@Override
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}
@Override
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);
}
}
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
if (mInLiveDataRegisterObserver) {
// We're in LiveData registration, which always results in a field change
// that we can ignore. The value will be read immediately after anyway, so
// there is no need to be dirty.
return;
}
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
requestRebind();
}
}
view更新逻辑
当viewmodel发生变化时,notifyPropertyChanged通过二进制或运算计算出要更新的属性,并回调监听器,最终执行到ActivityMainBinding中的onFieldChange方法.
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
public void notifyPropertyChanged(int fieldId) {
synchronized (this) {
if (mCallbacks == null) {
return;
}
}
mCallbacks.notifyCallbacks(this, fieldId, null);
}