databinding源码分析一
来源:互联网 发布:灯具品牌 知乎 编辑:程序博客网 时间:2024/06/16 09:42
前言
databinding是google在2015年发布的一个库,支持布局文件和mode数据之间进行绑定。最新版本已经支持双向绑定,数据的更新可以触发同步到布局对应的ui界面,布局文件的数据更新也可以传递到mode数据上。
1. 数据更新如何触发ui更新的?
2. ui操作是如何关联上数据的?
3. 自定义view该如何关联数据绑定?
带着以上三个问题分析源码,本篇先分析问题一:
源码
databinding源码分为两个组成部分:
Android/sdk/extras/android/m2repository/com/android/databinding
1. library
2. adapters
library为databinding核心组建代码,包括BaseObservable,DataBinderMapper,ViewDataBinding等
adapters定义了常用ui控件在与mode数据绑定时的适配器,如果是自定义控件,可以参照来编写。
以mvvm架构的天气工程为例子,clone代码到本地,切换到mvvm-databinding分之代码。
该工程代码使用了mvvm框架的架构,view和 mode关联是通过databinding框架在实现。在WeatherViewMode类定义了用来表示天气文本信息的字符串mWeatherinfo。
public class WeatherViewMode extends BaseObservable { public final ObservableField<String> mWeatherinfo = new ObservableField<>();
这个字符通过
ObservableField进行了包装。对应的布局文件
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <import type="android.view.View" /> <variable name="view" type="com.android_app_architecture_demo.weather.WeatherFragment" /> <variable name="viewmodel" type="com.android_app_architecture_demo.weather.WeatherViewMode" /> </data> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/weather_info" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewmodel.mWeatherinfo}" /> </FrameLayout></layout>
在布局文件声明了关联的view和viewmode,view对应的类是WeatherFragment
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment mFragmentWeatherBinding = FragmentWeatherBinding.inflate(inflater,container,false); mFragmentWeatherBinding.setView(this); mFragmentWeatherBinding.setViewmodel(mViewModel); View root = mFragmentWeatherBinding.getRoot(); return root; }
可以看到在WeatherFragment中通过FragmentWeatherBinding的类对当前view和viewmode进行了关联绑定。FragmentWeatherBinding这个类是编译过程中自动生成的,继承自ViewDataBinding。进入FragmentWeatherBinding来看一下具体绑定操作流程:
public FragmentWeatherBinding(android.databinding.DataBindingComponent bindingComponent, View root) { ... // listeners invalidateAll(); } @Override public void invalidateAll() { synchronized(this) { mDirtyFlags = 0x8L; } requestRebind(); }
FragmentWeatherBinding构造函数里面调用了invalidateAll进行界面刷新,进去发现是通过requestRebind来实现view的注册,view和viewmode之间的绑定。
protected void executeBindings() { ... updateRegistration(1, viewmodelMWeatherinfo); ... }
在requestRebind会最终调用executeBindings,executeBindings注册了天气信息viewmodelMWeatherinfo,对应的id是1。view和viewmode的id在后续的操作流程里面会继续使用到。
public void setViewmodel(com.android_app_architecture_demo.weather.WeatherViewMode Viewmodel) { //注册viewmode,对应id为0 updateRegistration(0, Viewmodel); this.mViewmodel = Viewmodel; synchronized(this) { mDirtyFlags |= 0x1L; } notifyPropertyChanged(BR.viewmodel); super.requestRebind(); }
setViewmodel方法里面注册viewmode,对应id为0。
updateRegistration(1,viewmodelMWeatherinfo)调用了父类ViewDataBinding的注册方法
/** * @hide */ protected boolean updateRegistration(int localFieldId, Observable observable) { return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER); }
其中CREATE_PROPERTY_LISTENER表示普通对象属性的监听器,对应的还有list和map对应的监听器,本例中只定义了一个String,因此匹配的是CREATE_PROPERTY_LISTENER。
/** * Method object extracted out to attach a listener to a bound Observable object. */ 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 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 WeakListener<Observable> getListener() { return mListener; } @Override public void addListener(Observable target) { target.addOnPropertyChangedCallback(this); } @Override public void removeListener(Observable target) { target.removeOnPropertyChangedCallback(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); } }
addListener是用来添加监听器
onPropertyChanged监听器回调,handleFieldChange用来处理对象属性数据变化以后的ui更新,后面再详细来看触发流程。现继续来分析注册监听器的流程
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; }
/** * @hide */ 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; } listener.setTarget(observable); } public void setTarget(T object) { unregister(); mTarget = object; if (mTarget != null) { mObservable.addListener(mTarget); } } @Override public void addListener(Observable target) { target.addOnPropertyChangedCallback(this); }
至此完成listner注册。
回头来看onPropertyChanged回调是如何触发ui进行更新的?以ObservableField为例,
public class ObservableField<T> extends BaseObservable implements Serializable { static final long serialVersionUID = 1L; private T mValue;
/** * Set the stored value. */ public void set(T value) { if (value != mValue) { mValue = value; notifyChange(); } }
当更改ObservableField的数据内容时候,会触发notifyChange,直接调用父类BaseObservable的实现
/** * Notifies listeners that all properties of this instance have changed. */ public void notifyChange() { synchronized (this) { if (mCallbacks == null) { return; } } mCallbacks.notifyCallbacks(this, 0, null); }
这里面的callback是什么呢?
@Override public void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) { synchronized (this) { if (mCallbacks == null) { mCallbacks = new PropertyChangeRegistry(); } } mCallbacks.add(callback); }
callback是OnPropertyChangedCallback,就是WeakPropertyListener里面的监听器。onPropertyChanged监听器回调,handleFieldChange用来处理ui更新。
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) { boolean result = onFieldChange(mLocalFieldId, object, fieldId); if (result) { requestRebind(); } }
首先调用onFieldChange(mLocalFieldId, object, fieldId),在本示例中,当天气信息变化,mLocalFieldId就是mWeatherinfo对应的id,object就是mWeatherinfo,fieldId在这里是0。
@Override protected boolean onFieldChange(int localFieldId, Object object, int fieldId) { switch (localFieldId) { case 0 : return onChangeViewmodel((com.android_app_architecture_demo.weather.WeatherViewMode) object, fieldId); case 1 : return onChangeViewmodelMWeatherinfo((android.databinding.ObservableField<java.lang.String>) object, fieldId); } return false; }
在onFieldChange处理中,首先检查localFieldId的值,在这里对应的是mWeatherinfo的id为1,继续调用
onChangeViewmodelMWeatherinfo。
private boolean onChangeViewmodelMWeatherinfo(android.databinding.ObservableField<java.lang.String> ViewmodelMWeatherinfo, int fieldId) { switch (fieldId) { case BR._all: { synchronized(this) { mDirtyFlags |= 0x2L; } return true; } } return false; }
检测到天气信息数据变化,设置mDirtyFlags |= 0x2L来标记。
handleFieldChange方法中判断,如果onChangeViewmodelMWeatherinfo返回true,即有数据更新,就开始调用requestRebind()
protected void requestRebind() { if (mContainingBinding != null) { mContainingBinding.requestRebind(); } else { synchronized (this) { if (mPendingRebind) { return; } mPendingRebind = true; } if (USE_CHOREOGRAPHER) { mChoreographer.postFrameCallback(mFrameCallback); } else { mUIThreadHandler.post(mRebindRunnable); } } }
最终通过UI线程mUIThreadHandler将数据刷新到对应的view上。
最终调用到FragmentWeatherBinding实现的方法executeBindings,这里完成UI的重新绘制。
@Override protected void executeBindings() { long dirtyFlags = 0; synchronized(this) { dirtyFlags = mDirtyFlags; mDirtyFlags = 0; } java.lang.String viewmodelMWeatherinfoGet = null; com.android_app_architecture_demo.weather.WeatherViewMode viewmodel = mViewmodel; android.databinding.ObservableField<java.lang.String> viewmodelMWeatherinfo = null; if ((dirtyFlags & 0xbL) != 0) { if (viewmodel != null) { // read viewmodel.mWeatherinfo viewmodelMWeatherinfo = viewmodel.mWeatherinfo; } updateRegistration(1, viewmodelMWeatherinfo); if (viewmodelMWeatherinfo != null) { // read viewmodel.mWeatherinfo.get() viewmodelMWeatherinfoGet = viewmodelMWeatherinfo.get(); } } // batch finished if ((dirtyFlags & 0xbL) != 0) { // api target 1 android.databinding.adapters.TextViewBindingAdapter.setText(this.weatherInfo, viewmodelMWeatherinfoGet); } }
总结
通过上文分析,可以大致看出databinding是如何注册管理view、viewmode,并且当viewmode数据更新如何通过监听器回调触发UI界面的更新。
- databinding源码分析一
- DataBinding学习笔记(一)源码分析
- todo-mvvm-databinding源码分析
- DataBinding(一)
- DataBinding源码解析
- DataBinding系列(一):DataBinding初认识
- DataBinding使用笔记一
- DataBinding学习(一)
- DataBinding 使用教程 一
- DataBinding笔记一
- quake2源码分析(一)
- Tomcat源码分析(一)
- Lua 源码分析(一)
- Jquery源码分析(一)
- 源码分析(一)
- KUIX源码分析一
- Logcat源码分析(一)
- gSOAP 源码分析(一)
- 3. Longest Substring Without Repeating Characters
- 使用python进行数据的采集
- [VS2013]如何闪开安装VS2013必须要有安装IE10的限制
- 【AtCoder CODE FESTIVAL 2017 qual C】D
- Python函数
- databinding源码分析一
- 测试csdn博客
- 2017 ACM-ICPC 沈阳站总结!
- 171023—各进制数输出:二进制转换&用格式控制符输出八,十,十六进制数
- 趣图:生产环境总是有意想不到的的异常
- Vue 2.5 发布,新特性一览
- 1024程序员节,聊聊程序人生,送专属T恤
- 前端 javascript DOM
- centos7 elk环境搭建