一、Databinding基本使用
实体类
class User(name: String) : BaseObservable() {
private var name: String
init {
this.name = name
}
@Bindable
fun getName(): String {
return name
}
fun setName(name: String) {
this.name = name
notifyPropertyChanged(BR.name)
}
}
布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.sun.jetpack.databinding.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.name}" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={user.name}" />
</LinearLayout>
</layout>
基本使用
class DataBindingTestActivity : AppCompatActivity() {
private lateinit var binding: ActivityDatabindingTestBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_databinding_test)
val user = User("sun")
binding.user = user
}
}
二、Databinding原理分析
Databinding使用了apt技术,我们build项目时Databinding会生成多个文件,我们可以在build文件中查看。
Databinding会将原有的activity_databinding_test.xml进行了拆分,分别是activity_databinding_test.xml和activity_databinding_test-layout.xml
通过上面的代码我们发现:Databinding将原有的layout和data标签去除了,并为根布局声明了一个layout/文件名_0的tag,为其它使用到@{}或@={}的控件按顺序添加了一个binding_X的tag。
该配置文件详细记述了:<Variables name="user" declared="true" type="com.sun.jetpack.databinding.User"> 我们声明的变量,变量指向的数据类型的绝对路径。<Target tag="binding_1" view="TextView"> tag对应的View类型。 <Expression attribute="android:text" text="user.name"> 控件绑定具体属性和Model中的具体属性。 <TwoWay>false</TwoWay> 是否是双向的。
接下来我们从 DataBindingUtil.setContentView(this,R.layout.activity_databinding_test) 入手
public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
int layoutId, @Nullable DataBindingComponent bindingComponent) {
activity.setContentView(layoutId);
View decorView = activity.getWindow().getDecorView();
ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}
方法中通过activity的setContentView加载布局,并通过window找到id为content的ViewGroup,它是一个FrameLayout用于加载我们添加的布局文件。接下来就是bindToAddedViews发方法。
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) {
final View childView = parent.getChildAt(endChildren - 1);
return bind(component, childView, layoutId);
} else {
final View[] children = new View[childrenAdded];
for (int i = 0; i < childrenAdded; i++) {
children[i] = parent.getChildAt(i + startChildren);
}
return bind(component, children, layoutId);
}
}
parent中的子View就是我们布局文件中的根布局LinearLayout,所以走if中的代码bind。
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}
bind方法又调用了DataBinderMapperImpl(apt生产的类)中的getDataBinder方法。
@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_ACTIVITYDATABINDINGTEST: {
if ("layout/activity_databinding_test_0".equals(tag)) {
return new ActivityDatabindingTestBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_databinding_test is invalid. Received: " + tag);
}
}
}
return null;
}
通过一系列条件判断之后返回一个ActivityDatabindingTestBindingImpl对象。
public ActivityDatabindingTestBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
//这里的3表示3个节点
this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
}
mapBindings调用了ViewDataBinding类中的mapBindings方法,在加载这个类的时候,会先执行静态块,调用onViewAttachedToWindow,所有没调用setUser之前数据绑定就是这么做的。
问题:没调用setUser之前数据绑定是怎么做的?ViewDataBinding类中有个静态块,调了onViewAttachedToWindow(v)
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) {
}
};
}
}
静态块中有个OnAttachStateChangeListener()监听事件,当控件状态发生改变,会执行一个Runnable,找到这个mRebindRunnable。
/**
* Runnable executed on animation heartbeat to rebind the dirty Views.
*/
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();
}
};
protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
Object[] bindings = new Object[numBindings];
mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
return bindings;
}
private static void mapBindings(DataBindingComponent bindingComponent, View view,
Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
boolean isRoot) {
//...
if (isRoot && tag != null && tag.startsWith("layout")) {
//...
bindings[index] = view;
//...
} else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
//...
bindings[tagIndex] = view;
//...
} else {
// Not a bound view
indexInIncludes = -1;
}
if (!isBound) {
//...
bindings[index] = view;
//...
}
if (view instanceof ViewGroup) {
//...
for (int i = 0; i < count; i++) {
//...
bindings[index] = DataBindingUtil.bind(bindingComponent, included,
layoutId);
//...
}
}
mapBindings方法匹配布局中含有databinding赋值的tag控件一一存入bindings的Object的数组中并返回。ViewDataBinding持有Activity或Fragment和View的引用,主要作用一次遍历View,实例化所有的子View,并存储到数组中,解决了findViewById性能问题,同时为我们省去了findViewById操作。
private ActivityDatabindingTestBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 1
);
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
this.mboundView1 = (android.widget.TextView) bindings[1];
this.mboundView1.setTag(null);
this.mboundView2 = (android.widget.EditText) bindings[2];
this.mboundView2.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x4L;
}
requestRebind();
}
将获取的View数组赋值给成员变量,执行了invalidateAll()方法,接着执行了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 {
mUIThreadHandler.post(mRebindRunnable);
}
}
}
protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
mBindingComponent = bindingComponent;
mLocalFieldObservers = new WeakListener[localFieldCount];
this.mRoot = root;
if (Looper.myLooper() == null) {
throw new IllegalStateException("DataBinding must be created in view's UI Thread");
}
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());
}
}
mChoreographer.postFrameCallback(mFrameCallback)实际上也执行了mRebindRunnable.run():
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();
}
};
接着看Runnable中的executePendingBindings()方法
/**
* Evaluates the pending bindings, updating any Views that have expressions bound to
* modified variables. This <b>must</b> be run on the UI thread.
*/
public void executePendingBindings() {
if (mContainingBinding == null) {
executeBindingsInternal();
} else {
mContainingBinding.executePendingBindings();
}
}
如果绑定了就不需要再绑定了,如果没有绑定就执行绑定。
/**
* Evaluates the pending bindings without executing the parent bindings.
*/
private void executeBindingsInternal() {
if (mIsExecutingPendingBindings) {
requestRebind();
return;
}
if (!hasPendingBindings()) {
return;
}
mIsExecutingPendingBindings = true;
mRebindHalted = false;
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBIND, null);
// The onRebindListeners will change mPendingHalted
if (mRebindHalted) {
mRebindCallbacks.notifyCallbacks(this, HALTED, null);
}
}
if (!mRebindHalted) {
executeBindings();
if (mRebindCallbacks != null) {
mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
}
}
mIsExecutingPendingBindings = false;
}
executeBindingsInternal方法对一些状态进行了判断添加回调,核心是executeBindings()方法,执行绑定操作。executeBindings()是一个抽象方法,它在哪实现的呢?我们回到build生成的ActivityDatabindingTestBindingImpl中。
核心代码
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String userName = null;
com.sun.jetpack.databinding.User user = mUser;
if ((dirtyFlags & 0x7L) != 0) {
if (user != null) {
// read user.name
userName = user.getName();
}
}
// batch finished
if ((dirtyFlags & 0x7L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, userName);
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView2, userName);
}
if ((dirtyFlags & 0x4L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView2, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, mboundView2androidTextAttrChanged);
}
}
dirtyFlags用于表示哪个属性发生变化,如果是M中的name被修改了 ,dirtyFlags & 0x7L —> 010 & 111 —> 010不为0,读取user.name,调用setText设置值,这是M—>V的过程。当(dirtyFlags & 0x4L) != 0成立,就是V—>M,它为双向绑定的控件添加了一个内容变化的监听mboundView2androidTextAttrChanged,当控件内容发生变化时,就会更新到Model上,这就是V—>M的过程。
private androidx.databinding.InverseBindingListener mboundView2androidTextAttrChanged = new androidx.databinding.InverseBindingListener() {
@Override
public void onChange() {
// Inverse of user.name
// is user.setName((java.lang.String) callbackArg_0)
java.lang.String callbackArg_0 = androidx.databinding.adapters.TextViewBindingAdapter.getTextString(mboundView2);
// localize variables for thread safety
// user.name
java.lang.String userName = null;
// user != null
boolean userJavaLangObjectNull = false;
// user
com.sun.jetpack.databinding.User user = mUser;
userJavaLangObjectNull = (user) != (null);
if (userJavaLangObjectNull) {
user.setName(((java.lang.String) (callbackArg_0)));
}
}
};
我们接着分析一下ViewModel上值的变化,当我们调用binding.setUser(user)时会执行ActivityDatabindingTestBindingImpl中的setUser方法
public void setUser(@Nullable com.sun.jetpack.databinding.User User) {
updateRegistration(0, User);
this.mUser = User;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.user);
super.requestRebind();
}
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
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 setLifecycleOwner(LifecycleOwner lifecycleOwner) {
}
@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);
}
}
private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
private final ObservableReference<T> mObservable;
protected final int mLocalFieldId;
private T mTarget;
public WeakListener(ViewDataBinding binder, int localFieldId,
ObservableReference<T> observable) {
super(binder, sReferenceQueue);
mLocalFieldId = localFieldId;
mObservable = observable;
}
//...
}
从上面可以看到CREATE_PROPERTY_LISTENER是一个CreateWeakListener对象,CreateWeakListener调用create得到WeakPropertyListener,WeakPropertyListener内有变量WeakListener,WeakListener持有ViewDataBinding和Observable(即VM User)。
接着看updateRegistration
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;
}
从mLocalFieldObservers中取localFieldId对应的WeakListener,如果为null就调用registerTo进行注册。与之前注册不一致就重新注册。
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;
if (mLifecycleOwner != null) {
listener.setLifecycleOwner(mLifecycleOwner);
}
}
listener.setTarget(observable);
}
public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
//调到上面的WeakPropertyListener中的addListener
mObservable.addListener(mTarget);
}
}
private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
implements ObservableReference<Observable> {
//...
@Override
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}
//...
}
registerTo方法把CreateWeakListener存储在mLocalFieldObservers里面。
listener.setTarget(observable)设置ViewModel的监听事件。
三、小结
1、DataBinding的关键类
- ActivityDatabindingTestBinding持有绑定类的引用。
- ActivityDatabindingTestBindingImpl具体实现了绑定。
- BR存储了VM的id,功能和R文件类似,还用于确定监听器。
- ViewDataBinding持有Activity或者Fragment和View的引用,主要作用是遍历View,实例化所有的子View,并存储在数组中,省去了findviewbyid的操作。
- DataBinderMapperImpl提供布局文件layoutId到ViewDataBinding类对应的映射,主要用于加载layout返回的ViewDataBinding对象
2、VM变化如何通知到View
设置监听:updateRegistration—>registerTo—>listener.setTarget(observable)—>target.addOnPropertyChangedCallback(this) 设置ViewModel的监听事件
VM—>V:notifyPropertyChanged(BR.id)—>mCallbacks.notifyCallbacks—>executeBindings
3、View变化如何同步到VM
设置监听:requestRebind—>executeBindings—>setTextWatcher设置对View的监听
V—>VM:回调InverseBindingListener的onChange设置值
4、流程图