jetpack02.databinding

148 阅读3分钟

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默认生成一些文件,如下图所示 image.png

  • 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);
}

整体依赖图

image.png