Jetpack:DataBinding

文章详细介绍了AndroidDataBinding的工作原理,包括布局中的数据绑定过程,如何通过XML标记和查找文件关联控件和数据,以及数据绑定的注册监听、UI刷新和数据驱动UI更新的流程。总结了DataBinding的使用注意事项和整个框架的核心机制。
摘要由CSDN通过智能技术生成

DataBinding 的使用

关于 DataBinding 如何使用 官方文档 已经有详细介绍,该篇文章主要讲解的 DataBinding 的原理,所以基本使用不会赘述。

个人使用 DataBinding 认为还是有使用的注意事项,DataBinding 框架虽然非常强大能处理很多数据绑定操作,但不建议将过多的操作都放在 xml 做数据绑定,应保持尽量以刷新 UI 更新的角度使用 DataBinding,否则当出现问题时,可能会存在难以定位的问题。

DataBinding 的布局绑定过程

在使用 DataBinding 时,为了能做到控件与数据的绑定,xml 会有如下更改:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
    
        <variable
            name="user"
            type="com.example.demo.User" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="#3C3B54"
        android:paddingLeft="5dp"
        android:paddingTop="7dp"
        android:paddingRight="5dp">

        <TextView
            android:id="@+id/text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}" />
    </LinearLayout>
</layout>

xml 的根节点是 <layout> 标签,<data> 标签提供数据相关的信息和类,数据绑定通过 @{} 描述说明。xml 文件描述都更改了,inflater dom 解析并不会识别新增的这些标签,那么 DataBinding 是怎么识别的?

/app/build/imtermediates/incremental/mergeDebugResources/stripped.dir/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#3C3B54"
    android:orientation="vertical"
    android:paddingLeft="5dp"
    android:paddingTop="7dp"
    android:paddingRight="5dp"
    android:tag="layout/activity_main_0">
    
    <!-- 根布局生成 tag 属性标记,用于创建界面的 DataBinding-->

	<!-- 数据绑定的位置生成了 tag 属性标记 -->
    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:tag="binding_1" /> 
</LinearLayout>

/app/build/imtermediates/data_binding_layout_info_type_merge/debug/out/activity_main-layout.xml

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Layout directory="layout" filePath="app/src/main/res/layout/activity_main.xml"
    isBindingData="true" isMerge="false" layout="activity_main"
    modulePackage="com.example.demo" rootNodeType="android.widget.LinearLayout">
    <Variables name="user" declared="true" type="com.example.demo.User">
        <location endLine="6" endOffset="42" startLine="4" startOffset="8" />
    </Variables>
    <Targets>
        <Target tag="layout/activity_main_0" view="LinearLayout">
            <Expressions />
            <location endLine="23" endOffset="18" startLine="9" startOffset="4" />
        </Target>
        <!-- 通过 binding_1 能找到绑定的控件及绑定的数据 -->
        <Target id="@+id/text_view" tag="binding_1" view="TextView">
            <Expressions>
                <Expression attribute="android:text" text="user.name">
                    <Location endLine="22" endOffset="38" startLine="22" startOffset="12" />
                    <TwoWay>false</TwoWay>
                    <ValueLocation endLine="22" endOffset="36" startLine="22" startOffset="28" />
                </Expression>
            </Expressions>
            <location endLine="22" endOffset="41" startLine="18" startOffset="8" />
        </Target>
    </Targets>
</Layout>

当我们将项目 build 重新构建时,DataBinding 会将我们的布局 xml 文件拆成了两个 xml 文件:

  • /app/build/imtermediates/incremental/mergeDebugResources/stripped.dir/layout/activity_main.xml

  • /app/build/imtermediates/data_binding_layout_info_type_merge/debug/out/activity_main-layout.xml

为了讲解方便,我将前者称为控件数据标记文件,后者称为控件数据查找文件。

DataBinding 将布局中使用了 @{} 的 View 生成 tag 属性标记,去控件数据查找文件就能查找到绑定信息。综合两个 xml 文件就能做到将控件和绑定信息的关系描述清楚。

如上例 TextView 生成了 tag 属性标记为 binding_1,当数据更新时就能通过 binding_1 找到属性 android:text 绑定的数据是 user.name。

或许你会有疑问:标记?查找绑定?那具体又是怎么个标记和查找绑定?

我们继续分析。先写一个简单使用 DataBinding 的 demo:

public class User extends BaseObservable {
    private String name;
    private String pwd;

    public User(String name, String pwd) {
        this.name = name;
        this.pwd = pwd;
    }

    @Bindable
    public String getName() {
        return name;
    }

    @Bindable
    public String getPwd() {
        return pwd;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
        notifyPropertyChanged(BR.pwd);
    }
}

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        binding.user = User("vincent", "12345")
        // binding.textView
    }
}

上面的 demo 非常简单,创建一个 User 对象,将 User 对象的属性和 xml 中的控件绑定。Activity 对界面的绑定也不是用的 setContentView(),而是通过 DataBindingUtils.setContentView()。

使用 DataBinding 的时候我们可以很方便的通过 binding.textView 的方式获取到控件,它是怎么做到的?DataBindingUtils.setContentView() 到底做了什么事情?

DataBindingUtils.java

public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
        int layoutId) {
    return setContentView(activity, layoutId, sDefaultComponent);
}

public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
        int layoutId, @Nullable DataBindingComponent bindingComponent) {
    // 还是调用 Activity 的 setContentView() 填充布局
    activity.setContentView(layoutId);
    View decorView = activity.getWindow().getDecorView();
    ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
    return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}

private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component,
        ViewGroup parent, int startChildren, int layoutId) {
    final int endChildren = parent.getChildCount();
    final int childrenAdded = endChildren - startChildren;
    if (childrenAdded == 1) {
    	// 如果我们的布局只有一个控件,调用 bind() 绑定
        final View childView = parent.getChildAt(endChildren - 1);
        return bind(component, childView, layoutId);
    } else {
    	// 如果布局有多个控件,循环获取布局的控件,然后 bind() 绑定
        final View[] children = new View[childrenAdded];
        for (int i = 0; i < childrenAdded; i++) {
            children[i] = parent.getChildAt(i + startChildren);
        }
        return bind(component, children, layoutId);
    }
}

// DataBinderMapperImpl 是 APT 生成的类
private static DataBinderMapper sMapper = new DataBinderMapperImpl();

static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View[] roots,
        int layoutId) {
    return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);
}

DataBindingUtils.setContentView() 通过源码分析,其实还是调用的 Activity 的 setContentView(),然后拿到 DecorView 下的 FrameLayout(android.R.id.content),将我们布局中的每个控件进行绑定。

其中 sMapper 是 APT 生成的类,文件生成在 /app/build/generated/ap_generated_sources/debug/out/com/example/demo/databinding/DataBindingMapperImpl.java

DataBindingMapperImpl.java

private static final int LAYOUT_ACTIVITYMAIN = 1;

private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(1);

static {
  INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.demo.R.layout.activity_main, LAYOUT_ACTIVITYMAIN);
}

@Override
public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
  int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
  if(localizedLayoutId > 0) {
    final Object tag = view.getTag();
    if(tag == null) {
      throw new RuntimeException("view must have a tag");
    }
    switch(localizedLayoutId) {
      case  LAYOUT_ACTIVITYMAIN: {
      	// /app/build/imtermediates/incremental/mergeDebugResources/stripped.dir/layout/activity_main.xml
      	// xml 文件的根节点 tag
        if ("layout/activity_main_0".equals(tag)) {
          // APT 生成的实现类
          return new ActivityMainBindingImpl(component, view);
        }
        throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
      }
    }
  }
  return null;
}

getDataBinder() 就做了一件事情,根据根布局生成的 tag 标记 layout/activity_main_0 创建对应界面的 ViewDataBinding 即 ActivityMainBindingImpl,在它的构造函数中就做了控件绑定的操作,让我们能够通过 binding.textView 的方式拿到控件:

ActivityMainBindingImpl.java

public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
	// 数字 2 表示布局绑定的控件数量
    this(bindingComponent, root, mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds));
}

private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
    super(bindingComponent, root, 1
        , (android.widget.TextView) bindings[1] // 控件绑定
        );
    this.mboundView0 = (android.widget.LinearLayout) bindings[0];
    this.mboundView0.setTag(null);
    this.textView.setTag(null);
    setRootTag(root);
    // listeners
    invalidateAll();
}

ViewDataBinding.java

protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
        int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
    Object[] bindings = new Object[numBindings];
    // mapBindings() 把 View 对象都存到 bindings 数组里面
    // 应用层就能够直接 binding.textView 使用
    mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
    return bindings;
}

构造函数中最主要的一句代码是 mapBindings(),该方法将我们布局中的 View 都存到数组中,最终应用层就可以通过 binding.textView 的方式拿到布局中的 View。

我们梳理下 DataBindingUtils.setContentView() 做了什么事情:

  • 通过 Activity 的 setContentView() 加载布局

  • 根据布局根节点的 tag 属性 layout/activity_xxx_0 创建对应布局的 ViewDataBinding

  • 在 ViewDataBinding 创建时遍历布局所有的 View 存到数组中,数组的 View 赋值给 ViewDataBinding 的控件变量。实现可以通过 binding 获取控件 View

DataBinding 是如何做到数据驱动 UI 的?

DataBinding 使用的是观察者模式,只是这个观察者模式的变种比较大。既然是观察者模式,还是会需要有注册、监听和通知三个操作。那么 DataBinding 是在什么时候开始做注册的?

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        binding.user = User("vincent", "12345")
        // binding.textView
    }
}

上面的代码是创建 ActivityMainBindingImpl 后,调用了 setUser() 将我们要提供数据的对象传给了 DataBinding。

ActivityMainBindingImpl.java

public void setUser(@Nullable com.example.demo.User User) {
    updateRegistration(0, User);
    this.mUser = User;
    synchronized(this) {
        mDirtyFlags |= 0x1L;
    }
    notifyPropertyChanged(BR.user);
    super.requestRebind();
}

上面的代码虽然很简短,但是却做了几个操作:

  • updateRegistration():注册监听流程,绑定观察者 observer 和被观察者 observable

  • mDirtyFlags:数据是否更新标志

  • super.requestRebind():请求执行界面刷新

注册监听流程

因为涉及的类比较多,在分析时我们需要时刻记得区分好观察者和被观察者,否则很容易会出现分析了流程,但是都不知道这些类是用来干嘛的。

ViewDataBinding.java

// 存储观察者的数组
private WeakListener[] mLocalFieldObservers;

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

// localFieldId:每个数据对象都会分配一个数据对象绑定 id
// observable:数据对象 User,被观察者
// CREATE_PROPERTY_LISTENER:用于创建 WeakPropertyListener 和 WeakListener
// WeakListener:持有 DataBinding、数据对象绑定 id、数据对象
protected boolean updateRegistration(int localFieldId, Observable observable) {
    return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}

private boolean updateRegistration(int localFieldId, Object observable,
        CreateWeakListener listenerCreator) {
    ...
    WeakListener listener = mLocalFieldObservers[localFieldId];
    if (listener == null) {
    	// 第一次获取为 null,走此处的注册过程
    	// listenerCreator 是 CREATE_PROPERTY_LISTENER
        registerTo(localFieldId, observable, listenerCreator);
        return true;
    }
    ...
    return true;
}

protected void registerTo(int localFieldId, Object observable,
        CreateWeakListener listenerCreator) {
    if (observable == null) {
        return;
    }
    WeakListener listener = mLocalFieldObservers[localFieldId];
    if (listener == null) {
    	// CREATE_PROPERTY_LISTENER.create() 创建 WeakPropertyListener
    	// WeakPropertyListener 会创建 WeakListener
        listener = listenerCreator.create(this, localFieldId);
        mLocalFieldObservers[localFieldId] = listener;
        ...
    }
    // 注册数据监听
    listener.setTarget(observable);
}

通过分析 updateRegistration() 我们列出几个比较重要信息和区分下观察者和被观察者:

  • localFieldId:数据对象绑定 id。用于查找存储 WeakListener 的数组索引

  • observable:数据对象 User,被观察者

  • WeakPropertyListener:持有 WeakListener

  • WeakListener:ViewDataBinding、数据对象绑定 id、数据对象的包装类,观察者。一个 WeakListener 对应一个数据对象

源码中 mLocalFieldObservers 虽然是存储 WeakListener 的观察者数组,通过源码分析它只是临时记录用于解除绑定操作,和数据绑定通知 UI 刷新无关,从注册流程角度分析我们可以忽略它的作用。

updateRegistration() 内调用 registerTo(),创建 WeakListener 并且存储到 mLocalFieldObservers 数组中。从这个角度分析,ViewDataBinding 是被观察者。

在这里插入图片描述

我们继续看 registerTo() 的 listener.setTarget(observable) 做了什么事情:

private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
        implements ObservableReference<Observable> {
    final WeakListener<Observable> mListener;

    public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
    	// 创建 WeakPropertyListener 的同时创建了 WeakListener
        mListener = new WeakListener<Observable>(binder, localFieldId, this);
    }

    @Override
    public WeakListener<Observable> getListener() {
        return mListener;
    }
	
  	@Override
    public void addListener(Observable target) {
    	// 注册数据更新监听回调,target 是 User 对象
        target.addOnPropertyChangedCallback(this);
    }

	@Override
    public void onPropertyChanged(Observable sender, int propertyId) {
    }
    ...
}

private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
    private final ObservableReference<T> mObservable;
    protected final int mLocalFieldId; // 数据对象绑定 id
    private T mTarget; // 数据对象

    public WeakListener(ViewDataBinding binder, int localFieldId,
            ObservableReference<T> observable) {
        super(binder, sReferenceQueue);
        mLocalFieldId = localFieldId;
        mObservable = observable; // WeakPropertyListener
    }

    public void setTarget(T object) {
        unregister();
        mTarget = object; // 记录数据对象 User
        if (mTarget != null) {
        	// 调用 WeakPropertyListener 的 addListener
            mObservable.addListener(mTarget);
        }
    }
	...
}

public class BaseObservable implements Observable {
    private transient PropertyChangeRegistry mCallbacks;

    public BaseObservable() {
    }

	// OnPropertyChangedCallback 就是 WeakPropertyListener
	// WeakPropertyListener 持有 WeakListener
	// WeakListener 保存有 ViewDataBinding、数据对象绑定 id、数据对象
    @Override
    public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
        synchronized (this) {
            if (mCallbacks == null) {
                mCallbacks = new PropertyChangeRegistry();
            }
        }
        mCallbacks.add(callback);
    }
}

public class PropertyChangeRegistry extends
        CallbackRegistry<Observable.OnPropertyChangedCallback, Observable, Void> {
}

public class CallbackRegistry<C, T, A> implements Cloneable {
	// 存储 WeakPropertyListener 的列表
    private List<C> mCallbacks = new ArrayList<C>();

    public synchronized void add(C callback) {
        if (callback == null) {
            throw new IllegalArgumentException("callback cannot be null");
        }
        int index = mCallbacks.lastIndexOf(callback);
        if (index < 0 || isRemoved(index)) {
            mCallbacks.add(callback);
        }
    }
}
  • PropertyChangeRegistry/CallbackRegistry:为了方便理解,可以将它理解为 WeakListener 观察者列表

listener.setTarget(observable) 其实就是注册观察者的操作,将 WeakPropertyListener 存储到 CallbackRegistry 的观察者列表。

在这里插入图片描述

简单总结下 updateRegistration() 就做了两件事情:

  • 为每一个需要绑定的数据对象提供一个数据对象绑定 id,创建 WeakListener,WeakListener 是 ViewDataBinding、数据对象绑定 id、数据对象 User 的包装类

  • 创建 CallbackRegistry,把它理解为 Observable 被观察者,将 WeakListener 注册到列表存储

为了方便理解,我们用伪代码描述下 updateRegistration():

public class ActivityMainBindingImpl {

	public void updateRegistration(User user) {
		registerTo(0, user);
	}	

	public void registerTo(int localFieldId, User user) {
		WeakListener listener = new WeakListener(this, localFieldId);
		listener.setTarget(user);
	}
}

public class WeakListener {
	private ViewDataBinding binder;
	private int localFieldId;
	private User user;
	
	public WeakListener(ViewDataBinding binder, int localFieldId) {
		this.binder = binder;
		this.localFieldId = localFieldId;
	}
	
	public void setTarget(User user) {
		user.addOnPropertyChangedCallback(this);
	}
}

public class User extends BaseObservable {
	// PropertyChangeRegistry/CallbackRegitry 本质上就是对这个观察者列表的操作
	private List<WeakListener> mCallbacks;

	// 其实就是注册观察者的操作
	public void addOnPropertyChangedCallback(WeakListener callback) {
		mCallbacks.add(callback);
	}
}

具体流程如下:

在这里插入图片描述

刷新 UI 流程

在上面经过 updateRegistration() 完成了注册观察者的操作后,setUser() 的流程还没结束:

ActivityMainBindingImpl.java

public void setUser(@Nullable com.example.demo.User User) {
    updateRegistration(0, User);
    this.mUser = User;
    synchronized(this) {
        mDirtyFlags |= 0x1L;
    }
    notifyPropertyChanged(BR.user);
    super.requestRebind();
}

紧接着修改了 mDirtyFlags,并且调用了 super.requestRebind():

protected void requestRebind() {
   	...
   	if (USE_CHOREOGRAPHER) {
    	mChoreographer.postFrameCallback(mFrameCallback);
    } else {
        mUIThreadHandler.post(mRebindRunnable);
    }
}

protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
    ...
    if (USE_CHOREOGRAPHER) {
    	// 通过编舞者每 16.6ms 接收一个 VSYNC 信号刷新 UI
        mChoreographer = Choreographer.getInstance();
        mFrameCallback = new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                mRebindRunnable.run();
            }
        };
    } else {
        mFrameCallback = null;
        mUIThreadHandler = new Handler(Looper.myLooper());
    }
}

private final Runnable mRebindRunnable = new Runnable() {
    @Override
    public void run() {
        ...
        executePendingBindings();
    }
};

public void executePendingBindings() {
    if (mContainingBinding == null) {
        executeBindingsInternal();
    } else {
        mContainingBinding.executePendingBindings();
    }
}

private void executeBindingsInternal() {
	...
	executeBindings();
	...
}

ActivityMainBindingImpl.java

@Override
protected void executeBindings() {
    long dirtyFlags = 0;
    synchronized(this) {
        dirtyFlags = mDirtyFlags;
        mDirtyFlags = 0;
    }
    java.lang.String userName = null;
    com.example.demo.User user = mUser;

	// 判断 dirtyFlags 是否有修改
    if ((dirtyFlags & 0x7L) != 0) {
	    if (user != null) {
	        // read user.name
	        userName = user.getName();
	    }
    }
    // batch finished
    if ((dirtyFlags & 0x7L) != 0) {
    	// 更新 UI
    	androidx.databinding.adapters.TextViewBindingAdapter.setText(this.textView, userName);
    }
}

DataBinding 刷新 UI 的方式是通过注册编舞者 Choreographer,每 16.6ms 接收一个 VSYNC 信号,再结合 mDirtyFlags 判断数值是否有修改,如果需要更新在下一个 VSYNC 信号到来时刷新 UI

通知更新 UI 流程

你可能有留意到在 setUser() 时我将 notifyPropertyChanged(BR.user) 忽略跳过了,其实是因为首次注册监听绑定时调用这句代码不会处理更新操作。但是我们数据更新通知刷新 UI 就是使用的该方法:

public class User extends BaseObservable {
    private String name;
    private String pwd;

    public User(String name, String pwd) {
        this.name = name;
        this.pwd = pwd;
    }

    @Bindable
    public String getName() {
        return name;
    }

    @Bindable
    public String getPwd() {
        return pwd;
    }

    public void setName(String name) {
        this.name = name;
        // 通知更新 UI
        notifyPropertyChanged(BR.name);
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
        // 通知更新 UI
        notifyPropertyChanged(BR.pwd);
    }
}

我们以 setName() 为例子讲解下 notifyPropertyChanged(id) 的原理:

BaseObservable.java

private transient PropertyChangeRegistry mCallbacks;

public void notifyPropertyChanged(int fieldId) {
    synchronized (this) {
        if (mCallbacks == null) {
            return;
        }
    }
    mCallbacks.notifyCallbacks(this, fieldId, null);
}

CallbackRegistry.java

public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
    ...
    // sender 是 User
    // arg 是 BR.name
    notifyRecurse(sender, arg, arg2);
    ...
}

private void notifyRecurse(T sender, int arg, A arg2) {
    ...
    // sender 是 User
    // arg 是 BR.name
    notifyRemainder(sender, arg, arg2, remainderIndex);
	...
}

private void notifyRemainder(T sender, int arg, A arg2, int remainderIndex) {
    if (remainderIndex < 0) {
    	// sender 是 User
    	// arg 是 BR.name
        notifyFirst64(sender, arg, arg2);
    } else {
        ...
    }
}

private void notifyFirst64(T sender, int arg, A arg2) {
    final int maxNotified = Math.min(Long.SIZE, mCallbacks.size());
    // sender 是 User
    // arg 是 BR.name
    notifyCallbacks(sender, arg, arg2, 0, maxNotified, mFirst64Removed);
}

private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex,
        final int endIndex, final long bits) {
    long bitMask = 1;
    for (int i = startIndex; i < endIndex; i++) {
        if ((bits & bitMask) == 0) {
        	// sender 是 User
        	// arg 是 BR.name
        	// mCallbacks.get(i) 拿到 BR.name 对应的 WeakPropertyListener
        	// mNotifier 是 PropertyChangeRegistry 的 NOTIFIER_CALLBACK
            mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
        }
        bitMask <<= 1;
    }
}

mNotifier 是 PropertyChangeRegistry 通过构造传入的 NOTIFIER_CALLBACK:

CallbackRegistry.java

private final NotifierCallback<C, T, A> mNotifier;

public CallbackRegistry(NotifierCallback<C, T, A> notifier) {
    mNotifier = notifier;
}

public class PropertyChangeRegistry extends
        CallbackRegistry<Observable.OnPropertyChangedCallback, Observable, Void> {

    private static final CallbackRegistry.NotifierCallback<Observable.OnPropertyChangedCallback, Observable, Void> NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback<Observable.OnPropertyChangedCallback, Observable, Void>() {
        @Override
        public void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observable sender,
                int arg, Void notUsed) {
            // callback 是 WeakPropertyListener
            // sender 是 User
            // arg 是 BR.name
            callback.onPropertyChanged(sender, arg);
        }
    };

    public PropertyChangeRegistry() {
        super(NOTIFIER_CALLBACK);
    }
}

WeakPropertyListener.java

final WeakListener<Observable> mListener;

@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 是 ActivityMainBindingImpl
    // sender 是 User
    // propertyId 是 BR.name
    binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}

ViewDataBinding.java

private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
    ...
    // mLocalFieldId = 0
    // object 是 User 
    // fieldId 是 BR.name
    boolean result = onFieldChange(mLocalFieldId, object, fieldId);
    if (result) {
    	// 等待下一次 VSYNC 信号到来时刷新 UI
        requestRebind();
    }
}

ActivityMainBindingImpl.java

@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
    switch (localFieldId) {
        case 0 :
            return onChangeUser((com.example.demo.User) object, fieldId);
    }
    return false;
}
private boolean onChangeUser(com.example.demo.User User, int fieldId) {
    if (fieldId == BR._all) {
        synchronized(this) {
                mDirtyFlags |= 0x1L;
        }
        return true;
    }
    else if (fieldId == BR.name) {
        synchronized(this) {
                mDirtyFlags |= 0x2L; // 修改数值,下一个 VSYNC 信号到来时刷新 UI
        }
        return true;
    }
    return false;
}

根据上面的源码分析,notifyPropertyChanged() 做了以下事情:

  • 根据数据对象绑定 id 从 CallbackRegistry 找到要通知的 WeakPropertyListener

  • WeakPropertyListener.onPropertyChanged() 拿着 WeakListener,WeakListener 对应一个数据对象,再根据 BR.name 找到 mDirtyFlags 修改数值

  • 如果判断 mDirtyFlags 数据需要更新,则等待下一个 VSYNC 信号到来时执行 UI 刷新

具体流程如下:

在这里插入图片描述

总结

该篇文章从布局绑定、注册监听、数据驱动 UI 更新各个流程都做了详细的源码分析,根据上面的内容我们再做对各流程做一个总结梳理。

布局绑定流程 DataBindingUtils.setContentView():

  • 通过 Activity 的 setContentView() 加载布局

  • 根据布局根节点的 tag 属性 layout/activity_xxx_0 创建对应布局的 ViewDataBinding

  • ViewDataBinding 创建时遍历布局所有的 View 存到数组中,数组的 View 赋值给 ViewDataBinding 的控件变量。实现可以通过 binding 获取控件 View

注册监听流程 updateRegistration():

  • 为每一个需要绑定的数据对象提供一个数据对象绑定 id,创建 WeakListener,WeakListener 是 ViewDataBinding、数据对象绑定 id、数据对象 User 的包装类

  • 创建 CallbackRegistry,把它理解为 Observable,将 WeakListener 注册到观察者列表,完成观察者和被观察者的绑定

UI 刷新流程 requestRebind():

DataBinding 刷新 UI 的方式是通过注册编舞者 Choreographer,每 16.6ms 接收一个 VSYNC 信号,再结合 mDirtyFlags 判断数值是否有修改,如果需要更新在下一个 VSYNC 信号到来时刷新 UI

数据驱动 UI 更新流程 notifyPropertyChanged():

  • 根据数据对象绑定 id 从 CallbackRegistry 找到要通知的 WeakPropertyListener

  • WeakPropertyListener.onPropertyChanged() 拿着 WeakListener,WeakListener 对应一个数据对象,再根据 propertyId 找到 mDirtyFlags 修改数值

  • 如果判断 mDirtyFlags 数据需要更新,则等待下一个 VSYNC 信号到来时执行 UI 刷新

我们将所有流程再梳理成伪代码,方便理解:

public class User extends BaseObservable {
	// PropertyChangeRegistry/CallbackRegitry 本质上就是对这个观察者列表的操作
	private List<WeakListener> mCallbacks;

	// 注册观察者
	public void addOnPropertyChangedCallback(WeakListener callback) {
		mCallbacks.add(callback);
	}

	public void setName(String name) {
		// 数据更新,通知刷新 UI
		notifyPropertyChanged(BR.name);
	}
	
	public void notifyPropertyChanged(int propertyId) {
		WeakListener listener = mCallbacks.get(0);
		int localFieldId = listener.localFieldId;
		boolean result = listener.binder.onFieldChanged(localFieldId, propertyId);
		if (result) {
			// 需要更新 UI,下一个 VSYNC 信号到来时执行 UI 刷新
			listener.binder.requestRebind();
		}
	}
}

public class ActivityMainBindingImpl {
	private User mUser;
	private int mDirtyFlags;

	// 注册监听流程
	public void updateRegistration(User user) {
		mUser = user;
		registerTo(0, user);
	}	

	public void registerTo(int localFieldId, User user) {
		WeakListener listener = new WeakListener(this, localFieldId);
		listener.setTarget(user);
	}
	
	// 收到数据更新需要刷新 UI 通知
	public boolean onFieldChanged(int localFieldId, int propertyId) {
		switch (localFieldId) {
			case 0:
				if (propertyId == BR.name) {
					// 修改 mDirtyFlags 数值,下一个 VSYNC 信号刷新 UI
					mDirtyFlags |= 0x2L; 
					return true;
				}
		}
		return false;
	}
	
	// 编舞者刷新 UI
	public void requestRebind() {
		mChoreographer.postFrameCallback(mFrameCallback);
	}

	public ActivityMainBindingImpl() {
    	mChoreographer = Choreographer.getInstance();
       	mFrameCallback = new Choreographer.FrameCallback() {
           @Override
           public void doFrame(long frameTimeNanos) {
               executeBindings();
           }
       };
	}

	public void executeBindings() {
		if ((mDirtyFlags & 0x2L) != 0) {
			textView.setText(mUser.getName());
    	}
	}
}

public class WeakListener {
	public ViewDataBinding binder;
	public int localFieldId;
	
	public WeakListener(ViewDataBinding binder, int localFieldId) {
		this.binder = binder;
		this.localFieldId = localFieldId;
	}
	
	// 注册监听,将观察者和被观察者绑定
	public void setTarget(User user) {
		user.addOnPropertyChangedCallback(this);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值