DataBinding原理分析
DataBinding即数据绑定,是 Google 在 Jetpack 中推出的一款数据绑定的支持库,利用该库可以实现在页面组件中直接绑定应用程序的数据源。使其维护起来更加方便,架构更明确简介。
1 简单使用
开启DataBinding
在app的build.gradle中添加如下代码:
android {
.....
dataBinding{
enabled true
}
}
User.class
public class User extends BaseObservable {
private String name;
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="uesr"
type="com.example.binding.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{uesr.name}" />
</LinearLayout>
</layout>
MainActivity.class
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
User user = new User();
test.setName("echo");
binding.setUesr(user);
2 原理分析
2.1拆分布局文件
首先编译器会首先把布局文件进行拆分,生成的文件目录路径为/app/build/intermediates/data_binding_layout_info_type_merge/debug/mergeDebugResources/out/activity_main-layout.xml
第一部分:
<?xml version="1.0" encoding="utf-8"?>
<Layout layout="activity_main" modulePackage="com.mzw.myrouter" filePath="/Users/echo/demo/Binding/app/src/main/res/layout/activity_main.xml" directory="layout" isMerge="false">
<!--先前在activity_main.xml中声明的对象user-->
<Variables declared="true" type="com.example.binding.User" name="uesr">
<location startLine="6" startOffset="8" endLine="8" endOffset="42"/>
</Variables>
<Targets>
<!-- textview 线形布局 并自动加上了tag = layout/activity_main_0-->
<Target tag="layout/activity_main_0" view="LinearLayout">
<Expressions/>
<location startLine="12" startOffset="4" endLine="24" endOffset="18"/>
</Target>
<!-- textview 并自动加上了tag = binding_1-->
<Target id="@+id/text" tag="binding_1" view="TextView">
<Expressions>
// android:text = "@{user.name}"
<Expression text="uesr.name" attribute="android:text">
<Location startLine="22" startOffset="12" endLine="22" endOffset="38"/>
<TwoWay>false</TwoWay>
<ValueLocation startLine="22" startOffset="28" endLine="22" endOffset="36"/>
</Expression>
</Expressions>
<location startLine="18" startOffset="8" endLine="22" endOffset="41"/>
</Target>
</Targets>
</Layout>
第二部分的路径是app/build/intermediates/incremental/mergeDebugResources/stripped.dir/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
<!--对应上面自动生成的tag-->
tools:context=".MainActivity" android:tag="layout/activity_main_0"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<!--对应上面自动生成的tag-->
android:tag="binding_1" />
</LinearLayout>
可以看到第二部分的activity_main.xml对比在项目中自己的activity_main.xml 少了data标签下面的内容。也就是说拆分成了data和view两部分。当在view部分解析到tag=binding_1的时候,就可以去data下面通过这个tag找到<Target id="@+id/text" tag="binding_1" view="TextView">和<Expression text="uesr.name" attribute="android:text">,就可以知道TextView的"android:text"需要的数据是uesr.name,这样在执行的时候就可以去user类当中找到user.name属性。
2.2 引用关系
/app/build/generated/data_binding_base_class_source_out/debug/dataBindingGenBaseClassesDebug/out/com/example/binding/databinding目录下,存放着ActivityMainBinding.java。
public abstract class ActivityMainBinding extends ViewDataBinding {
@NonNull
public final TextView text;
@Bindable
protected User mUesr;
protected ActivityMainBinding(Object _bindingComponent, View _root, int _localFieldCount,
TextView text) {
super(_bindingComponent, _root, _localFieldCount);
this.text = text;
}
public abstract void setUesr(@Nullable User uesr);
......
}
可以看到是一个抽象类,并且可以看到我们调用的setUser()方法,那么具体的逻辑就一定在子类当中。子类的存放的路径为:
app/build/generated/ap_generated_sources/debug/out/com/example/binding。
代码包结构
先前在User类中我们在setName()方法中调用了notifyPropertyChanged(BR.name),那么打开BR.java。
public class BR {
public static final int _all = 0;
public static final int name = 1;
public static final int uesr = 2;
}
可以看到编译器自动生成了这些文件,有点类似于R文件,就是用来记录所有的data信息的。下面来看ActivityMainBindingImpl.java的部分代码
public class ActivityMainBindingImpl extends ActivityMainBinding {
public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
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.text.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
@Override
public boolean setVariable(int variableId, @Nullable Object variable) {
boolean variableSet = true;
if (BR.uesr == variableId) {
setUesr((com.example.binding.User) variable);
}
else {
variableSet = false;
}
return variableSet;
}
public void setUesr(@Nullable com.example.binding.User Uesr) {
// 更新注册信息
updateRegistration(0, Uesr);
// 保存user
this.mUesr = Uesr;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.uesr);
super.requestRebind();
}
}
可以看到setUser()就是之前在MainActivity中调用的方法。由于保存user对象,那么ActivityMainBindingImpl就持有了User的引用。接下来看一下updateRegistration(0, Uesr)都干了什么。
ViewDataBinding.java
// 静态常量 负责创建WeakPropertyListener
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
}
};
// ViewDataBinding 的内部类 对WeakListener<Observable>进行了一层包装
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);
}
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
private boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
// observable为null,也就是传入的user对象为null 就反注册listener
if (observable == null) {
return unregisterFrom(localFieldId);
}
// 反之则中数组中根据下标去取listener
// 下标就是BR文件中声明的int常量
WeakListener listener = mLocalFieldObservers[localFieldId];
// 不存在 就创建一个存入数组当中 并把observable传给listener
if (listener == null) {
registerTo(localFieldId, observable, listenerCreator);
return true;
}
// 如果数组中之前就存在对应的listener 且对象一样 则do nothing
if (listener.getTarget() == observable) {
return false;//nothing to do, same object
}
// 如果数组中之前就存在对应的listener 且对象不一样 则反注册
// 并为新对象重新创建一个listener存入数组
unregisterFrom(localFieldId);
registerTo(localFieldId, observable, listenerCreator);
return true;
}
引用关系
通过ActivityMainBindingbinding = DataBindingUtil.setContentView(this, R.layout.activity_main)这一行代码Activity和ActivityMainBindingbinding就相互持有对方的引用又因为ActivityMainBindingbinding继承自ViewDataBinding。所以Activity和ViewDataBinding也相互持有对方的引用。WeakListener和ViewDataBinding通过前面提到的数组的关系,也存在互相引用关系。那WeakListener和WeakPropertyListener同样也存在相互引用关系。ViewDataBinding在之前this.mUser = user时也持有了User的引用。最后User也间接的持有了WeakPropertyListener的引用。
引用关系理明白了,那么当User中的name变化的时候,就会是User-> WeakPropertyListener-> WeakListener-> ViewDataBinding-> Activity,最终TextView上的文字就更新了。
2.3 流程分析
从DataBindingUtil.setContentView()入手
public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
int layoutId) {
return setContentView(activity, layoutId, sDefaultComponent);
}
//省略中间调用的部分代码
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View[] roots,
int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);
}
可以看到最后是调用了DataBindingUtil内部的静态方法bind()。sMapper是DataBinderMapper,这是一个抽象类,getDataBinder()是一个抽象方法,下面是具体的实现:
DataBinderMapperImpl.java // 就是上方代码包结构图片中中最下面的那个类
@Override
public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
if(localizedLayoutId > 0) {
// 得到view上的tag
final Object tag = view.getTag();
if(tag == null) {
throw new RuntimeException("view must have a tag");
}
switch(localizedLayoutId) {
case LAYOUT_ACTIVITYMAIN: {
// 这里就与上面的activity_main.xml中的自动给LinearLayout添加的tag对应上了
if ("layout/activity_main_0".equals(tag)) {
// 生成对应的ViewBinding
return new ActivityMainBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
}
}
}
return null;
}
下面看ActivityMainBindingImpl中都做了什么:
public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
//内部调用了下面三个参数的构造方法
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.text这里就解释了为什么Databinding可以通过binding点上view的id就可以找到控件了
this.text.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
接着看一下其中mapBindings()方法都干了什么:
protected static Object[] mapBindings(DataBindingComponent bindingComponent, View[] roots, int numBindings, ViewDataBinding.IncludedLayouts includes, SparseIntArray viewsWithIds) {
// 初始化一个数组 长度为numBindings
Object[] bindings = new Object[numBindings];
for(int i = 0; i < roots.length; ++i) {
mapBindings(bindingComponent, roots[i], bindings, includes, viewsWithIds, true);
}
return bindings;
}
private static void mapBindings(DataBindingComponent bindingComponent, View view, Object[] bindings, ViewDataBinding.IncludedLayouts includes, SparseIntArray viewsWithIds, boolean isRoot) {
// return v != null ? (ViewDataBinding)v.getTag(id.dataBinding) : null;
// 上面是getBinding()所做的事情,就是根据tag去取view对应的binding
ViewDataBinding existingBinding = getBinding(view);
// 如果之前没有生成binding才会去做下面的解析
if (existingBinding == null) {
Object objTag = view.getTag();
String tag = objTag instanceof String ? (String)objTag : null;
boolean isBound = false;
int indexInIncludes;
int id;
int count;
//是根布局并且以tag是以layout开头 也就是LinearLayout的tag="layout/activity_main_0"
if (isRoot && tag != null && tag.startsWith("layout")) {
id = tag.lastIndexOf(95);
if (id > 0 && isNumeric(tag, id + 1)) {
count = parseTagInt(tag, id + 1);
if (bindings[count] == null) {
// 把LinearLayout存入数组
bindings[count] = view;
}
indexInIncludes = includes == null ? -1 : count;
isBound = true;
} else {
indexInIncludes = -1;
}
// tag是以binding_开头 也就是TextView的tag="binding_1"
} else if (tag != null && tag.startsWith("binding_")) {
id = parseTagInt(tag, BINDING_NUMBER_START);
if (bindings[id] == null) {
// TextView 存入数组
bindings[id] = view;
}
isBound = true;
indexInIncludes = includes == null ? -1 : id;
} else {
indexInIncludes = -1;
}
...... // 省略部分代码
// 递归调用 例如ViewGroup嵌套ViewGroup 就会递归调用直到遍历完所有的view
if (!isInclude) {
mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
}
}
}
}
}
可以看到最终就是把所有的view解析出来并存入到数组当中。到此XML文件中的内容就被全部解析出来了。
2.4 数据绑定
拿到了所有的view,也拿到了数据User类,下面就来看一下是如何把数据绑定到view上的。
ViewDataBinding.java
static { // 静态代码块
if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
ROOT_REATTACHED_LISTENER = null;
} else {
ROOT_REATTACHED_LISTENER = new OnAttachStateChangeListener() {
@TargetApi(VERSION_CODES.KITKAT)
@Override
public void onViewAttachedToWindow(View v) {
// execute the pending bindings.
final ViewDataBinding binding = getBinding(v);
// 这是一个任务
binding.mRebindRunnable.run();
v.removeOnAttachStateChangeListener(this);
}
@Override
public void onViewDetachedFromWindow(View v) {
}
};
}
private final Runnable mRebindRunnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
mPendingRebind = false;
}
processReferenceQueue();
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
// Nested so that we don't get a lint warning in IntelliJ
if (!mRoot.isAttachedToWindow()) {
// Don't execute the pending bindings until the View
// is attached again.
mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
return;
}
}
// 执行绑定
executePendingBindings();
}
};
// 最终会调到这个抽象方法
// 那么我们就需要去ActivityMainBindingImpl.java查看具体实现
protected abstract void executeBindings();
ActivityMainBindingImpl.java
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
// 取出之前保存的user对象
com.example.binding.User uesr = mUesr;
// 声明一个空的String
java.lang.String uesrName = null;
// 如果有多个view则会有多个这样的if
if ((dirtyFlags & 0x7L) != 0) {
if (uesr != null) {
//从user中取出name的值并赋值
uesrName = uesr.getName();
}
}
// batch finished
if ((dirtyFlags & 0x7L) != 0) {
//调用BindingAdapter的setText
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.text, uesrName);
}
}
TextViewBindingAdapter.java
@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
final CharSequence oldText = view.getText();
if (text == oldText || (text == null && oldText.length() == 0)) {
return;
}
if (text instanceof Spanned) {
if (text.equals(oldText)) {
return; // No change in the spans, so don't set anything.
}
} else if (!haveContentsChanged(text, oldText)) {
return; // No content changes, so don't set anything.
}
// 设置到view上
view.setText(text);
}
3 真正的推手
前面看到做出绑定动作都是在一个runnable当中,那么谁才是真正执行着个任务的人呢?不知道是否还记得在User类当中的setName()方法中的notifyPropertyChanged(BR.name)。就是这一行代码使整个工作得以推进。
BaseObservable.java
public void notifyPropertyChanged(int fieldId) {
synchronized (this) {
if (mCallbacks == null) {
return;
}
}
// PropertyChangeRegistry mCallbacks 是BaseObservable的成员变量
mCallbacks.notifyCallbacks(this, fieldId, null);
}
CallbackRegistry.java
public synchronized void notifyCallbacks(T sender, int arg, A arg2) {}
...... // 省略一些回调步骤 最终会回调到下面的方法
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) {
// mNotifier 是CallbackRegistry的内部抽象类NotifierCallback
mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
}
bitMask <<= 1;
}
}
PropertyChangeRegistry.java
private static final NotifierCallback<OnPropertyChangedCallback, Observable, Void> NOTIFIER_CALLBACK = new NotifierCallback<OnPropertyChangedCallback, Observable, Void>() {
public void onNotifyCallback(OnPropertyChangedCallback callback, Observable sender, int arg, Void notUsed) {
// 回调到这里
callback.onPropertyChanged(sender, arg);
}
};
PropertyChangeRegistry.java是不是很眼熟?对,这就是上面BaseObservable.java的成员属性,绕了一圈又回来了。别急接下来还得绕。
public interface Observable {
void addOnPropertyChangedCallback(OnPropertyChangedCallback callback);
void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback);
abstract class OnPropertyChangedCallback {
// 回调到此处
public abstract void onPropertyChanged(Observable sender, int propertyId);
}
}
点进去PropertyChangeRegistry.java的onPropertyChanged)最后又回到了BaseObservable.java的父类Observable的内部类当中了。这个内部类其中的一个实现类就是WeakPropertyListener
private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
...... //省略部分代码
@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?
}
// 调用了ViewDataBinding的方法
binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
}
}
ViewDataBinding.java
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
if (mInLiveDataRegisterObserver) {
// We're in LiveData registration, which always results in a field change
// that we can ignore. The value will be read immediately after anyway, so
// there is no need to be dirty.
return;
}
// 这是一个抽象方法 具体实现在ActivityMainBindingImpl当中
// 判断对象或值是否发生了改变
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
// 如果发生了变化
if (result) {
requestRebind();
}
}
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
final LifecycleOwner owner = this.mLifecycleOwner;
if (owner != null) {
Lifecycle.State state = owner.getLifecycle().getCurrentState();
if (!state.isAtLeast(Lifecycle.State.STARTED)) {
return; // wait until lifecycle owner is started
}
}
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
if (USE_CHOREOGRAPHER) {
mChoreographer.postFrameCallback(mFrameCallback);
} else {
// 调用主线程的handler发送任务
mUIThreadHandler.post(mRebindRunnable);
}
}
}
可以看到经过层层回调,最终是发了一个handler来推动2.4部分的数据绑定的整个流程。