Android的DataBinding原理介绍
Activity在inflate layout时,通过DataBindingUtil来生成绑定,从代码看,是遍历contentView得到View数组对象,然后通过数据绑定library生成对应的Binding类,含Views、变量、listeners等。生成类位于
build/intermediates/classes/debug/…package…/databinding/xxx.Java 下,具体如何生成这里暂不作深入。
绑定过程
- 首先,会在父类(ViewDataBinding)中实例化回调或Handler,用于之后的绑定操作;
- private static final boolean USE_CHOREOGRAPHER = SDK_INT >= 16;
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());
}
- 接着,通过调用 mapBindings(…) 遍历布局以获得包含bound、includes、ID Views的数组对象,再依次赋给对应View
- final Object[] bindings = mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds);
this.mboundView0 = (Android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
- 然后,调用 invalidateAll() -> requestRebind() -> … -> mRebindRunnable.run() – 执行 Runnable
- // 用于动态重新绑定 Views
private final Runnable mRebindRunnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
mPendingRebind = false;
}
.....
executePendingBindings();
}
};
- 最后,通过该Runnable会执行到 executePendingBindings() -> … -> executeBindings(),在这里会执行绑定相关操作。
- @Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags; // mDirtyFlags 变量更新的标志
mDirtyFlags = 0;
}
.....
}
设置变量(数据对象)
普通 Java bean 对象
- 首先,通过mDirtyFlags标识变量(所有变量共用)
- synchronized(this) {
mDirtyFlags |= 0x1L;
}
- 然后,调用 notifyPropertyChanged(…) 来通知更新(若有回调)
- public void notifyPropertyChanged(int fieldId) {
if (mCallbacks != null) {
mCallbacks.notifyCallbacks(this, fieldId, null);
}
}
- 最后,调用 requestRebind() -> … -> executeBindings() 再次执行绑定操作,将数据更新到Views上
- @Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
.....
}
Observable 对象
- 在设置变量时,会先调用 updateRegistration(..) 注册一个Observable对象的监听
- public void setContact(com.connorlin.databinding.model.ObservableContact contact) {
updateRegistration(0, contact);
this.mContact = contact;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.contact);
super.requestRebind();
}
- 其他步骤同普通 Java bean 对象
ObservableFields 对象
- 前期步骤同普通 Java Bean 对象
- 与 Observable 对象不同的是,Observable对象的监听是在 executeBindings() 中注册的
- @Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
...
if ((dirtyFlags & 0xfL) != 0) {
if ((dirtyFlags & 0xdL) != 0) {
if (contact != null) {
// read contact.mName
mNameContact = contact.mName;
}
updateRegistration(0, mNameContact);
if (mNameContact != null) {
// read contact.mName.get()
mNameContact1 = mNameContact.get();
}
}
...
}
...
}
注册Observable对象监听
- 入口 updateRegistration(0, contact) :
- protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
private boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
...
// 确保不重复监听,先移除再添加观察监听
unregisterFrom(localFieldId);
registerTo(localFieldId, observable, listenerCreator);
return true;
}
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return;
}
// 创建对象监听并存到mLocalFieldObservers中
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
// CREATE_PROPERTY_LISTENER -> create(...)
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
}
// 将监听绑定到Observable对象上
listener.setTarget(observable);
}
每个Observable对象都会添加一个观察监听,保存在数组 mLocalFieldObservers 中,并以 localFieldId 索引。
- CREATE_PROPERTY_LISTENER 为何物?
- private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
// 返回从WeakPropertyListener实例中获取的监听器(WeakListener)
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) {
// WeakPropertyListener 继承于 Observable.OnPropertyChangedCallback,
// 所以 this 其实就是 Observable对象的属性监听器
target.addOnPropertyChangedCallback(this);
}
...
}
private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
private final ObservableReference<T> mObservable;
protected final int mLocalFieldId;
private T mTarget;
...
public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
// mObservable 是上面的 WeakPropertyListener对象
// mTarget 是绑定到listener上得Observable对象
mObservable.addListener(mTarget);
}
}
...
}
CREATE_PROPERTY_LISTENER 实际上只是一个接口实例,注册时会调用它的create()方法创建一个弱引用listener,它的作用是将listener绑定到Observable对象上,
绑定时,会调用 listener.setTarget(…) 将Observable对象传给 WeakPropertyListener实例,然后,WeakPropertyListener 会为 Observable对象添加OnPropertyChangedCallback。
- addOnPropertyChangedCallback实现
addOnPropertyChangedCallback 在 BaseObservable中实现,首先会实例化一个PropertyChangeRegistry对象,同时创建一个用来通知Observable对象重新绑定更新的回调CallbackRegistry.NotifierCallback。然后将 OnPropertyChangedCallback 添加到PropertyChangeRegistry的回调列表中
- @Override
public synchronized void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
mCallbacks.add(callback);
}
这样,注册Observable对象的监听就完毕了。
更新(重新绑定)Observable对象
设置或更新Observable对象时都会调用notifyPropertyChanged()或notifyChange()来通知更新,那到底是如何更新的呢?
- 回调过程
- public void notifyPropertyChanged(int fieldId) {
// mCallbacks 是 PropertyChangeRegistry对象,在 addOnPropertyChangedCallback 时实例化
// 如果注册了Observable对象监听,那么mCallbacks不为null
if (mCallbacks != null) {
mCallbacks.notifyCallbacks(this, fieldId, null);
}
}
// baseLibrary
private void notifyCallbacks(T sender, int arg, A arg2, int startIndex, int endIndex, long bits) {
long bitMask = 1L;
for(int i = startIndex; i < endIndex; ++i) {
if((bits & bitMask) == 0L) {
// mNotifier 是实例化PropertyChangeRegistry时创建的
// mNotifier 即 CallbackRegistry.NotifierCallback
this.mNotifier.onNotifyCallback(this.mCallbacks.get(i), sender, arg, arg2);
}
bitMask <<= 1;
}
}
// PropertyChangeRegistry.NOTIFIER_CALLBACK
public void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observable sender,
int arg, Void notUsed) {
// callback 是为Observable对象添加的OnPropertyChangedCallback,即WeakPropertyListener
callback.onPropertyChanged(sender, arg);
}
// WeakPropertyListener
public void onPropertyChanged(Observable sender, int propertyId) {
// binder 即生成的Binding类对象
ViewDataBinding binder = mListener.getBinder();
...
binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
// onFieldChange 实现在生成的Binding类中
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
// 如果对象属性变化,将重新绑定
requestRebind();
}
}
通过 notifyPropertyChanged 调用到 mNotifier 回调, mNotifier 通知OnPropertyChangedCallback Observable对象属性发生变化,然后在onPropertyChanged中又转给ViewDataBinding对象(生成的Binding类)处理。
- 判断是否需要重新绑定并执行,在生成的Binding类中实现
- // 生成的Binding类中得方法
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
// 如果变量不是Observable类型或没有添加 Bindable注解,就不会判断,直接返回false
switch (localFieldId) {
case 0 :
return onChangeContact((com.connorlin.databinding.model.ObservableContact) object, fieldId);
}
return false;
}
private boolean onChangeContact(com.connorlin.databinding.model.ObservableContact contact, int fieldId) {
switch (fieldId) {
case BR.name: {
synchronized(this) {
mDirtyFlags |= 0x4L;// 通过mDirtyFlags判断对象是否变化
}
return true;
}
...
}
return false;
}
至此,更新过程完毕。
整个注册与更新过程可以用一张流程图来概括:

事件处理
事件处理的原理很简单,在生成Binding类中会实现View事件的监听,在构造时实例化View的事件监听,然后在绑定时将事件监听对象赋值给对应View,这样,点击时就会触发相应的监听。
这里以 DataBindingDemo 中 EventActivity部分为例:
- 生成的Binding类并实现View的事件监听
- public class ActivityEventBinding extends Android.databinding.ViewDataBinding
implements Android.databinding.generated.callback.OnCheckedChangeListener.Listener,
Android.databinding.generated.callback.OnClickListener.Listener {
// Checkbox check监听
private final Android.widget.CompoundButton.OnCheckedChangeListener mCallback3;
private final Android.view.View.OnClickListener mCallback2;
private final Android.view.View.OnClickListener mCallback1;
// listeners
private OnClickListenerImpl mAndroidViewViewOnCl;
...
// Listener Stub Implementations
public static class OnClickListenerImpl implements Android.view.View.OnClickListener{
private com.connorlin.databinding.handler.EventHandler value;
public OnClickListenerImpl setValue(com.connorlin.databinding.handler.EventHandler value) {
this.value = value;
return value == null ? null : this;
}
@Override
public void onClick(Android.view.View arg0) {
this.value.onClickFriend(arg0);
}
}
...
}
- 实例化View的事件监听
- public ActivityEventBinding(Android.databinding.DataBindingComponent bindingComponent, View root) {
super(bindingComponent, root, 0);
...
// listeners
mCallback3 = new Android.databinding.generated.callback.OnCheckedChangeListener(this, 3);
mCallback2 = new Android.databinding.generated.callback.OnClickListener(this, 2);
mCallback1 = new Android.databinding.generated.callback.OnClickListener(this, 1);
invalidateAll();
}
- 在执行绑定中绑定View事件监听
- @Override
protected void executeBindings() {
...
if ((dirtyFlags & 0x6L) != 0) {
if (handler != null) {
// read handler::onClickFriend
AndroidViewViewOnCli = (((mAndroidViewViewOnCl == null)
? (mAndroidViewViewOnCl = new OnClickListenerImpl()) : mAndroidViewViewOnCl).setValue(handler));
}
}
// batch finished
if ((dirtyFlags & 0x6L) != 0) {
this.mboundView1.setOnClickListener(AndroidViewViewOnCli);
}
if ((dirtyFlags & 0x4L) != 0) {
this.mboundView2.setOnClickListener(mCallback1);
this.mboundView3.setOnClickListener(mCallback2);
Android.databinding.adapters.CompoundButtonBindingAdapter.setListeners(
this.mboundView4, mCallback3, (Android.databinding.InverseBindingListener)null);
}
}
- 触发事件并执行
ViewStub
原理类似,只是利用 ViewStubProxy 来延迟绑定。
- 使用layout中的ViewStub实例化一个ViewStubProxy对象赋给viewstub变量,并与Bingding关联
- public ActivityViewStubBinding(Android.databinding.DataBindingComponent bindingComponent, View root) {
super(bindingComponent, root, 0);
final Object[] bindings = mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds);
...
this.viewStub = new Android.databinding.ViewStubProxy((Android.view.ViewStub) bindings[1]);
this.viewStub.setContainingBinding(this);
...
}
- 实例化ViewStubProxy的同时会注册inflate监听
- private OnInflateListener mProxyListener = new OnInflateListener() {
@Override
public void onInflate(ViewStub stub, View inflated) {
mRoot = inflated;
mViewDataBinding = DataBindingUtil.bind(mContainingBinding.mBindingComponent,
inflated, stub.getLayoutResource());
mViewStub = null;
if (mOnInflateListener != null) {
mOnInflateListener.onInflate(stub, inflated);
mOnInflateListener = null;
}
mContainingBinding.invalidateAll();
mContainingBinding.forceExecuteBindings();
}
};
public ViewStubProxy(ViewStub viewStub) {
mViewStub = viewStub;
mViewStub.setOnInflateListener(mProxyListener);
}
- inflate ViewStub
- if (!mActivityViewStubBinding.viewStub.isInflated()) {
mActivityViewStubBinding.viewStub.getViewStub().inflate();
}
当ViewStub infate时,执行mProxyListener,其中会生成ViewStub的Binding,并强制执行主Binding重绑
- 绑定ViewStub
- @Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
// batch finished
if (viewStub.getBinding() != null) {
viewStub.getBinding().executePendingBindings();
}
}
这样,ViewStub绑定就结束了。
Android的DataBinding原理介绍的更多相关文章
- android 编译的原理介绍
http://blog.csdn.net/mr_raptor/article/details/7540066
- Android Animation学习(一) Property Animation原理介绍和API简介
Android Animation学习(一) Property Animation介绍 Android Animation Android framework提供了两种动画系统: property a ...
- android MultiDex multidex原理原理下遇见的N个深坑(二)
android MultiDex 原理下遇见的N个深坑(二) 这是在一个论坛看到的问题,其实你不知道MultiDex到底有多坑. 不了解的可以先看上篇文章:android MultiDex multi ...
- Android数据绑定DataBinding(二)入门篇
前言 之前写了Android数据绑定DataBinding(一)入门篇,很简单的记录了如何使用DataBinding,其初衷是想要代码中的数据发生改变,不需要繁琐的setText等操作,在最后说到了只 ...
- Android数据绑定DataBinding(一)入门篇
早上看到了一篇推文"还在使用繁琐的findViewById,跟着尝试了一下DataBinding,之前使用ButteKnife就已经干掉了findViewById.因为写过微信小程序,那种在 ...
- 03 Yarn 原理介绍
Yarn 原理介绍 大纲: Hadoop 架构介绍 YARN 产生的背景 YARN 基础架构及原理 Hadoop的1.X架构的介绍 在1.x中的NameNodes只可能有一个,虽然可以通过Se ...
- 04 MapReduce原理介绍
大数据实战(上) # MapReduce原理介绍 大纲: * Mapreduce介绍 * MapReduce2运行原理 * shuffle及排序 定义 * Mapreduce 最早是由googl ...
- Android四大基本组件介绍与生命周期
Android四大基本组件介绍与生命周期 Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器 ...
- Android Testing学习01 介绍 测试测什么 测试的类型
Android Testing学习01 介绍 测试测什么 测试的类型 Android 测试 测什么 1.Activity的生命周期事件 应该测试Activity的生命周期事件处理. 如果你的Activ ...
随机推荐
- Cloud TPU Demos(TensorFlow 云 TPU 样例代码)
Cloud TPU Demos 这是一个Python脚本的集合,适合在开源TensorFlow和 Cloud TPU 上运行. 如果您想对模型做出任何修改或改进,请提交一个 PR ! https:// ...
- hibernate4整合spring3出现java.lang.NoClassDefFoundError: [Lorg/hibernate/engine/FilterDefinition;
解决办法 原先:<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annota ...
- 关于return的一些了解
写return是一种清晰的风格,可以防止一些意外的错误. 所以书上只说应该写,而不是必须写. 如果符合某个条件要退出的话,可以用return返回,否则可以不写这句代码的,当程序执行到"}&q ...
- 线性表 linear_list 顺序存储结构
可以把线性表看作一串珠子 序列:指其中的元素是有序的 注意last和length变量的内在关系 注意:将元素所占的空间和表长合并为C语言的一个结构类型 静态分配的方式,分配给一个固定大小的存储空间之后 ...
- Java编写高质量代码改善程序的151个建议
第一章 Java开发中通用的方法和准则 建议1:不要在常量和变量中出现易混淆的字母: (i.l.1:o.0等). 建议2:莫让常量蜕变成变量: (代码运行工程中不要改变常量值). 建议3:三元操作符 ...
- 77. Combinations(medium, backtrack, 重要, 弄了1小时)
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. For exampl ...
- python学习之路前端-JavaScript
JavaScript简介 JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本 ...
- Node.js Net 模块
Node.js Net 模块提供了一些用于底层的网络通信的小工具,包含了创建服务器/客户端的方法,我们可以通过以下方式引入该模块: var net = require("net") ...
- PHP MySQL 简介
PHP MySQL 简介 通过 PHP,您可以连接和操作数据库. MySQL 是跟 PHP 配套使用的最流行的开源数据库系统. 如果想学习更多 MySQL 知识可以查看本站MySQL 教程. MySQ ...
- ELK 6安装配置 nginx日志收集 kabana汉化
#ELK 6安装配置 nginx日志收集 kabana汉化 #环境 centos 7.4 ,ELK 6 ,单节点 #服务端 Logstash 收集,过滤 Elasticsearch 存储,索引日志 K ...