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
// 用于动态重新绑定 Viewsprivate final Runnable mRebindRunnable = new Runnable() {    @Override    public void run() {        synchronized (this) {            mPendingRebind = false;        }        .....        executePendingBindings();    }};
  • 最后,通过该Runnable会执行到 executePendingBindings() -> … -> executeBindings(),在这里会执行绑定相关操作。
@Overrideprotected 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上
@Overrideprotected 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() 中注册的
@Overrideprotected 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的回调列表中

@Overridepublic 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);    }}

// baseLibraryprivate 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_CALLBACKpublic void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observable sender,        int arg, Void notUsed) {    // callback 是为Observable对象添加的OnPropertyChangedCallback,即WeakPropertyListener    callback.onPropertyChanged(sender, arg);}

// WeakPropertyListenerpublic 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事件监听
@Overrideprotected 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
@Overrideprotected void executeBindings() {    long dirtyFlags = 0;    synchronized(this) {        dirtyFlags = mDirtyFlags;        mDirtyFlags = 0;    }    // batch finished    if (viewStub.getBinding() != null) {        viewStub.getBinding().executePendingBindings();    }}

这样,ViewStub绑定就结束了。

时间: 2024-09-24 22:58:20

Android的DataBinding原理介绍的相关文章

Android 属性动画原理与DataBinding

Android 属性动画原理与DataBinding 看到这个标题的时候你可能会有疑问,属性动画和 DataBinding 之间有什么关系?我个人理解的是:它们内部的实现思想有相似之处.这篇文章主要对 Android 属性动画的知识通过文字进行整理记录,内容参考于<Android开发艺术探索>,在最后会给出我如此理解属性动画和 DataBinding 的原因. Android动画概述: Android 的动画可以分为三种:View 动画.帧动画和属性动画,View 动画通过对场景里的对象不断做

干货满满,Android热修复方案介绍

摘要:在技术直播中,阿里云客户端工程师李亚洲(毕言)从技术原理层面解析和比较了业界几大热修复方案,揭开了Qxxx方案.Instant Run以及阿里Sophix等热修复方案的神秘面纱,帮助大家更加深刻地理解了代码插桩.全量dex替换.资源修复等常见场景解决方案,本文干货满满,精彩不容错过. 以下内容根据演讲视频以及PPT整理而成. 视频分享链接,点击这里! 在传统的修复模式下,如果线上的App出现Bug之后进行修复所需要的时间成本非常高,这是因为往往需要发布一个新的版本,然后将其发布到对应的应用

Android代码入侵原理解析(一)

Android代码入侵原理解析(一)           1.代码入侵原理 代码入侵,或者叫代码注入,指的是让目标应用/进程执行指定的代码.代码入侵,可以在应用进行运行过程中进行动态分析,也是对应用进行攻击的一种常见方式.我把代码入侵分为两种类型:静态和动态.静态代码入侵是直接修改相关代码,在应用启动和运行之前,指定代码就已经和应用代码关联起来.动态代码入侵是应用启动之后,控制应用运行进程,动态加载和运行指定代码. 2.静态代码入侵 静态代码入侵,有直接和间接的手段. 直接手段是修改应用本身代码

android MultiDex multidex原理原理下遇见的N个深坑(二)

android MultiDex 原理下遇见的N个深坑(二) 这是在一个论坛看到的问题,其实你不知道MultiDex到底有多坑. 不了解的可以先看上篇文章:android MultiDex multidex原理(一) 解决和遇到的其它问题,请见下一篇文章:android MultiDex 原理下超出方法数的限制问题(三) 遭遇multidex  愉快地写着Android代码的总悟君往工程里引入了一个默默无闻的jar然后Run了一下~~~~ 经过漫长的等待AndroidStudio构建失败了.于是

Android应用性能优化最佳实践.2.1 Android系统显示原理

绘?制?优?化 Android应用启动慢,使用时经常卡顿,是非常影响用户体验的,应该尽量避免出现.卡顿的场景有很多,按场景可以分成4类:UI绘制.应用启动.页面跳转.事件响应,如 图2-1所示.在这四种场景下又有多个小分类,基本上覆盖了卡顿的各个场景.   图2-1 卡顿主要场景 这4种卡顿场景的根本原因又可以分成两大类. 界面绘制:主要原因是绘制的层级深.页面复杂.刷新不合理,由于这些原因导致卡顿的场景更多出现在UI和启动后的初始界面以及跳转到页面的绘制上. 数据处理:导致这种卡顿场景的原因是

Android Studio(一):介绍、安装、配置

Android Studio相关博客: Android Studio(一):介绍.安装.配置 Android Studio(二):快捷键设置.插件安装 Android Studio(三):设置Android Studio编码 Android Studio(四):Android Studio集成Genymotion Android Studio(五):修改Android Studio项目包名 Android Studio(六):Android Studio添加注释模板 Android Studio

在Linux中绑定多网卡的原理介绍与实战

一.原理介绍: 1.什么是bonding? Linux bonding 驱动提供了一个把多个网络接口设备捆绑为单个的网络接口设置来使用,用于网络负载均衡及网络冗余: Linux双网卡绑定实现就是使用两块网卡虚拟成为一块网卡,这个聚合起来的设备看起来是一个单独的以太网接口设备,通俗点讲就是两块网卡具有相同的IP地址而并行链接聚合成一个逻辑链路工作. 2.bonding技术功能作用? bonding主要用于两个方向: a.实现 负载均衡:   通过bonding技术,将网络的业务流量平均分配到不同的

jquery选择器原理介绍

 这篇文章主要介绍了jquery选择器原理介绍($()使用方法),需要的朋友可以参考下 每次申明一个jQuery对象的时候,返回的是jQuery.prototype.init对象,很多人就会不明白,init明明是jQuery.fn的方法啊,实际上这里不是方法,而是init的构造函数,因为js的prototype对象可以实现继承,加上js的对象只是引用不会是拷贝,new jQuery,new jQuery.fn和new jQuery.fn.init的子对象是一样的,只是有没有执行到init的不同.

android MultiDex multiDex原理(一)

android MultiDex 原理(一) Android分包MultiDex原理详解 转载请注明:http://blog.csdn.net/djy1992/article/details/51162013 下一篇文章:android MultiDex 原理下遇见的N个深坑(二) MultiDex的产生背景          当Android系统安装一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOpt.DexOpt的执行过程是在第一次加载Dex文件的时候执行