双向绑定原理

  • 前言
  • 双向绑定
  • ViewDataBinding分析
  • 双向原理
  • 总结


博客创建时间:2021.04.12
博客更新时间:2021.04.13

以Android studio build=4.1.3,gradle=6.5,SdkVersion 30来分析讲解。如图文和网上其他资料不一致,可能是别的资料版本较低而已


前言

在早期的DataBinding双向绑定中需要防止死循环造成ANR,解决方案是需要判断前后两次值变动的id值是否一样。但是在现在的高版本中该bug已在底层中结局,我们在文章后面会详细说明。

双向绑定

为什么需要双向绑定?

假设一个登陆应用场景:

Android双向列表左右联动效果_Android开发

  1. 打开应用,登录页面会自动填充上次登录保存User的name 和password。
user.name="test"
    user.password="123456"
    binding.user=user
    
	public class User {
	 // 用户名
	public String name;
	 // 密码
	 public String password;
	}
  1. 如果此时想换一个用户登陆,常规方法是Edittext.getText获得name和password,然后进行登陆操作
  2. 现在使用双向绑定。
<EditText
        android:id="@+id/etName"
        android:text="@={user.name}"
        android:inputType="text" />

    <EditText
        android:id="@+id/etPassword"
        android:text="@={user.password}"/>

@={}就是双向绑定符

  1. 现在只要我们对name和password EditText输入内容进行改变分别为“admin”和“987654321”,那么user中对应的两个字段的值就会自动变动。
  2. 进行登陆操作时依然只用拿user来用就可以了。点击登陆按钮测试值
else if(v?.id  ==R.id.btnLogin){
  Log.i("1111","name="+user.name+"  password="+user.password)
}

// 输出
name=admin  password=987654321

ViewDataBinding分析

每个Fragment或Activity的布局开启DataBinding后都会生成一个ViewDataBinding子类。我们来分析其中关键源码

1. ActivityMainBinding 的初始化
ActivityMainBinding进行相关初始化设置,绑定数据与DataBinding的关联

private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
        //1. ActivityMainBinding 布局界面初始化,包括相关的值赋予
        invalidateAll();
    }

    /**
     * 初始化布局,并对控件的设值赋值
     */
    @Override
    public void invalidateAll() {
        synchronized(this) {
                mDirtyFlags = 0x10L;
        }
        // 请求重新绑定,主要是对控件的各值显示进行变动
        requestRebind();
    }

2. 指定变量赋值

public void setLiveData(@Nullable com.xuanyuan.databindinganalysis.MyObservableField LiveData) {
        updateRegistration(0, LiveData);
        this.mLiveData = LiveData;
        synchronized(this) {
            mDirtyFlags |= 0x1L;
        }
        notifyPropertyChanged(BR.liveData);
        super.requestRebind();
    }

    public void setClick(@Nullable android.view.View.OnClickListener Click) {
        this.mClick = Click;
        synchronized(this) {
            mDirtyFlags |= 0x4L;
        }
        notifyPropertyChanged(BR.click);
        super.requestRebind();
    }

3. 通知更新指定变量

// 控件的各值进行确定赋予
    @Override
    public boolean setVariable(int variableId, @Nullable Object variable)  {
        boolean variableSet = true;
        else if (BR.liveData == variableId) {
            setLiveData((com.xuanyuan.databindinganalysis.MyObservableField) variable);
        } else {
            variableSet = false;
        }
            return variableSet;
    }

4. 变量Change,通知UI刷新

@Override
    protected void executeBindings() {
    ...
        if ((dirtyFlags & 0x11L) != 0) {
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.etName, liveDataGet);
        }
        if ((dirtyFlags & 0x10L) != 0) {
            // api target 1
       androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.etName, null,null, etNameandroidTextAttrChanged);
        }
    }

双向原理

当我们使用"@={}"双向绑定符时,观测ViewBindingIml代码。

1.常规双向绑定

private androidx.databinding.InverseBindingListener etNameandroidTextAttrChanged = new androidx.databinding.InverseBindingListener() {
        @Override
        public void onChange() {
            java.lang.String callbackArg_0 = androidx.databinding.adapters.TextViewBindingAdapter.getTextString(etName);
            java.lang.String userName = null;
            boolean userJavaLangObjectNull = false;
            com.xuanyuan.databindinganalysis.User user = mUser;
            userJavaLangObjectNull = (user) != (null);
            if (userJavaLangObjectNull) {
                user.setName(((java.lang.String) (callbackArg_0)));
            }
        }
    };

	TextViewBindingAdapter.setTextWatcher(this.etName, null, null, null, etNameandroidTextAttrChanged);

其实这种双向绑定只是调用了 view.addTextChangedListener(newValue),对内容变动后的监听操作处理,源码中自动帮我们处理了。

2. Observable双向绑定

val livaValue = MyObservableField()
 
 override fun onClick(v: View?) {
      if (v?.id == R.id.btnLogin) {
            val value = Math.random() * 1300
            livaValue.set(value.toString())
     }
 }
 
 <EditText
     android:id="@+id/etName"
     android:layout_width="match_parent"
     android:text="@={liveData}"
     android:inputType="text" />
  1. 当调用livaValue.set()时,阅读源码会发现有更新回调,最终执行ViewBinding的executeBindings()方法
@Override
protected void executeBindings() {
...
}
  1. 更新调用后,EditText中的值变化,会触发TextChangedListener再次调用livaValue.set(value.toString()。此时EditText中的值变化中的值不再变化

总结

本文主要讲解了双向绑定的意义,如何使用双向绑定。

对于DataBinding的分析需要查看对应ViewDataBinding类,即可透过现象看本质。