Android DataBinding框架的使用以及简单分析

转载请标明出处https://www.jianshu.com/p/fb8d33168f57

前言:毕业快3年了,出来工作之后一直是倾向于 Android相关平台逆向以及广告SDK的开发和学习,一直想补一下Android开发相关的知识点,国庆前群里老哥们聊起了MVVM和DataBinding,我心里终究是按耐不住好奇心了,于是乎也打开了浏览器,学了起来,其实本来一开始是想学MVVM先,毕竟databinding也是配合MVVM的一套工具,一种实现。但是机缘巧合先学起了这个,后面再补一篇MVVM的学习。

1.DataBinding有什么用,能解决什么问题

DataBinding是一个Google官方发布的框架,是为了应对日常开发中数据层和UI层之间的传递的。我们日常开发中需要自己手动去写findViewById,setOnClickListener这类厚重的代码。这个框架就可以简便的实现数据和UI的传递,UI对数据的监听,UI的刷新。

2.DataBinding的使用

DataBinding作为官方直推,更是不用添加依赖使用,而是gradle直接支持了,我们只需要在项目app下build.gradle之中的android下添加下面代码就可以使用,如果添加了之后无法使用,可以尝试升级AS版本。

android {
    dataBinding {
        enabled = true;
    }
}

3.DataBinding的代码实现

3.1.视图与数据变量的绑定


        
        
    


DataBinding对XML布局格式有一定的要求,需要在xml最外层使用layout包裹,之后里面分别写入data以及正常UI布局。其中data标签中声明你变量名以及变量类,我这里就是声明了自己写的TextBean类,命名为text。供下面使用。

3.2.视图数据的使用以及控件事件监听

DataBinding会根据activity和xml自动生成一个ViewDataBinding类,这个类负责了view的管理和逻辑,事件监听以及变量的管理。我这个类是放在build文件夹下,这个看看它是如何实现的。


ViewDataBinding.jpg

之后在想要操作view的类中进行viewDataBinding的实例化,设置数据。之后XML可以通过@{}的格式配合变量名获取,比方说@{text.title}。

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ActivityMainBinding viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        textBean = new TextBean();
        textBean.setTitle("dataBinding你好");
        textBean.setText("dataBinding你好");
        viewDataBinding.setActivity(this);
        viewDataBinding.setText(textBean);
    }
 

这个text就是传递过去的“dataBinding你好”。
除了数据的传递,还有对于view事件的监听,比如点击事件。


这里就是调用了传递进去的activity中的updateTitle方法,这里activity::updateTitle这种格式的调用,规定这个方法必须要是一个有View参数的方法。

public void updateTitle(View view) {
        textBean.setTitle("dataBinding你好" + i);
        i++;
    }

当然这个activity完全可以是你想要调用的任意类,updateTitle也可以是类里的任意方法。只要你在data里绑定了数据。
除此之外,还有其他比如@{() -> activity.updateTitle(obj)},这种写法无论带不带参数,方法参数对应上即可。所以其实也可以实现在xml中传递参数到方法里。

3.3.BindingAdapter,视图控件对于数据变化监听。

BindingAdapter是DataBinding提供的一个用于控件对于变量发生变化的监听的。

@BindingAdapter("android:text_title_check")
    public static void checkTitle(TextView view, CharSequence text) {
        CharSequence oldText = view.getText();
        if (!haveContentsChanged(text, oldText)) {
            return; // 数据没有变化不进行刷新视图
        }
        view.setText(text);
    }

这里@BindingAdapter()中可以根据你自己的需要进行命名,方法名也可以随意,但是要注意参数以及XML中调用时的对应。


这样子,当传入BindingAdapter标注的方法的变量发生变化时,就会调用该方法,该方法可以进行一定的逻辑操作,比如图片的加载,文本的判定。

3.4.单向和双向数据绑定

前面我们使用了单向数据绑定,数据变化引起视图Textview中的更新。
DataBinding提供了BaseObservable、ObservableField、ObservableCollection三种方式实现。

我这里使用BaseObservable为例。

public class TextBean extends BaseObservable implements Serializable {
    private String title;
    private String text;


    @Bindable
    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
        notifyPropertyChanged(BR.title);
    }

    @Bindable
    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
        // 需要手动刷新
        notifyPropertyChanged(BR.text);
    }
}

Bindable注解get方法,notifyPropertyChanged在set方法刷新。也可以使用ObservableField这个类,对BaseObservable绑定和解绑的进一步封装。
BaseObservable是官方实现的观察器,该接口自己实现了绑定和解绑,我们只需要专注于数据的传递更新。
这样子配合@{}就可以达到数据变化,视图也实时变化。

但是我现在有一个需求,我现在有一个输入框editText,或者一个多选框,这个框的结果我必须知道并拿来使用,正常的开发中,我们会通过获取控件之后getText之类的方法获取框中的实时内容,很麻烦。但是DataBinding提供了我们双向绑定。我们只需要通过@={}的写法,就可以实现
视图改变==>数据实时改变。
数据改变==>视图实时改变。


效果如下:
demo.gif

像这样,点击textview的事件中对数据进行修改,由于textview单向绑定,editText双向绑定,都指向同一个数据,所以都实时的改变。editText修改的时候也同理。

3.5.DataBinding自提供变量

如果现在我们有一个需求,需要在布局中使用上下文的变量,那我们该怎么做。正常思路就是继续使用data,Variable标签。
其实DataBinding自己也提供了一些Variable的,比方说context。 是可以直接使用的. 代表View的getContext()。


我这里就配合上面讲的控件事件,将context.applicationContext.packageName作为一个string参数,传递给了packageName方法。

 public void packageName(String packageName) {
        Toast.makeText(this, packageName, Toast.LENGTH_SHORT).show();
    }
contextToast.jpg
3.6.DataBinding和include的使用

关于include的使用,其实主要是主activity和包含布局之间要使用 bind来传递数据。包含布局中也要用data和variable实现数据变量和view的绑定。注意bind:xxx和variable name="xxx",这里两者xxx必须是一样的才能匹配到。



   
        
    

    
    

4.DataBinding的简单分析

DataBinding的简单使用可以说是很简单,省去了很多代码,简化了数据和视图之间的交互。不禁我们就想知道,他其实是怎么实现的,我也很好奇,然后点开了源码,看的云里雾里,但是大概是,DataBinding对自己定义的结构的xml进行了解析,配合activity创建ViewDataBinding,获取各种view以及监听数据的过程。

ActivityMainBinding对象的生成

由DataBinding对象获取的方法DataBindingUtil.setContentView最终追溯到这个方法。

public class DataBindingUtil {
static  T bind(DataBindingComponent bindingComponent, View root,
            int layoutId) {
        return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
    }
}

这个方法最终返回的其实就是相对应ActivityMainBindingImpl的对象。
ActivityMainBindingImpl.jpg

ViewDataBinding实例化:

 protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
        mBindingComponent = bindingComponent;
        mLocalFieldObservers = new WeakListener[localFieldCount];
        this.mRoot = root;
        if (Looper.myLooper() == null) {
            throw new IllegalStateException("DataBinding must be created in view's UI Thread");
        }
        if (USE_CHOREOGRAPHER) {
            mChoreographer = Choreographer.getInstance();
            mFrameCallback = new Choreographer.FrameCallback() {
                @Override
                public void doFrame(long frameTimeNanos) {
                    mRebindRunnable.run();
                }
            };
        } else {
            mFrameCallback = null;
            mUIThreadHandler = new Handler(Looper.myLooper());
        }
    }

并且ViewDataBinding会通过view创建对应的WeakListener,弱引用类型接口作为观察者,利于被回收,同时很好的处理内存泄露的情况。之后则是update时的逻辑。

@Override
    public boolean setVariable(int variableId, @Nullable Object variable)  {
        boolean variableSet = true;
        if (BR.activity == variableId) {
            setActivity((com.llyyyw.databinding.MainActivity) variable);
        }
        else if (BR.text == variableId) {
            setText((com.llyyyw.databinding.TextBean) variable);
        }
        else {
            variableSet = false;
        }
            return variableSet;
    }
    public void setText(@Nullable com.llyyyw.databinding.TextBean Text) {
        updateRegistration(0, Text);
        this.mText = Text;
        synchronized(this) {
            mDirtyFlags |= 0x1L;
        }
        notifyPropertyChanged(BR.text);
        super.requestRebind();
    }
/**
     * @hide
     */
    protected boolean updateRegistration(int localFieldId, Observable observable) {
        return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
    }

    /**
     * @hide
     */
    protected boolean updateRegistration(int localFieldId, ObservableList observable) {
        return updateRegistration(localFieldId, observable, CREATE_LIST_LISTENER);
    }

    /**
     * @hide
     */
    protected boolean updateRegistration(int localFieldId, ObservableMap observable) {
        return updateRegistration(localFieldId, observable, CREATE_MAP_LISTENER);
    }
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
            return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
        }
    };

    private static final CreateWeakListener CREATE_LIST_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
            return new WeakListListener(viewDataBinding, localFieldId).getListener();
        }
    };

    private static final CreateWeakListener CREATE_MAP_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
            return new WeakMapListener(viewDataBinding, localFieldId).getListener();
        }
    };

最终流程是:

源代码全部不好贴出来,口述一下。相对应ActivityMainBindingImpl的实例化,数据绑定在视图UI线程中创建,以及调用父类ViewDataBinding的executePendingBindings直到executeBindings这个抽象方法,指向的是ActivityMainBindingImpl的executeBindings方法,注册WeakListener监听。
当我们设置数据,BindingImpl的setVariable调用控件set方法,调用updateRegistration监听。当BaseObservable变化,触发notifyPropertyChanged,触发回调,也就是对应的WeakListener。就会更新视图。

这里我们讲完了数据变化从而view变化的原理,那view变化引起数据变化呢。
首先要知道ViewDataBinding是如何具体保存view的。前面说明了ActivityMainBindingImpl才是整个DataBinding的核心实现类,也调用了它的executeBindings方法,这个方法里其实就是对控件的一系列操作,控件的设值,控件的各种监听事件。

        if ((dirtyFlags & 0x15L) != 0) {
            // api target 1
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.etTitle, textTitle);
            com.llyyyw.databinding.dbAdapter.TextBindingAdapter.checkTitle(this.tvTitle2, textTitle);
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tvTitle2, textTitle);
        }
        if ((dirtyFlags & 0x10L) != 0) {
            // api target 1
            androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.etTitle, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, etTitleandroidTextAttrChanged);
            this.tvTitle.setOnClickListener(mCallback1);
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tvTitle, contextApplicationContextPackageName);
        }
        if ((dirtyFlags & 0x19L) != 0) {
            // api target 1
            this.mboundView01.setTextText(textText);
        }
        if ((dirtyFlags & 0x12L) != 0) {
            // api target 1
            this.tvTitle2.setOnClickListener(activityUpdateTitleAndroidViewViewOnClickListener);
        }
private androidx.databinding.InverseBindingListener etTitleandroidTextAttrChanged = new androidx.databinding.InverseBindingListener() {
        @Override
        public void onChange() {
            // Inverse of text.title
            //         is text.setTitle((java.lang.String) callbackArg_0)
            java.lang.String callbackArg_0 = androidx.databinding.adapters.TextViewBindingAdapter.getTextString(etTitle);
            // localize variables for thread safety
            // text.title
            java.lang.String textTitle = null;
            // text
            com.llyyyw.databinding.TextBean text = mText;
            // text != null
            boolean textJavaLangObjectNull = false;



            textJavaLangObjectNull = (text) != (null);
            if (textJavaLangObjectNull) {




                text.setTitle(((java.lang.String) (callbackArg_0)));
            }
        }
    };

只看其中的EditText控件可以发现,DataBinding是这样通过etTitleandroidTextAttrChanged这个监听器去实时获取控件的值进而对数据进行实时更新的。

5.总结

DataBinding的使用并不难,当然DataBinding还有许多其他的使用。深入去了解DataBinding可以发现里面的大奥秘,我这里云里雾里也只能理解这么冰山一角。学习使我快乐!

你可能感兴趣的:(Android DataBinding框架的使用以及简单分析)