一湃交、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技術(shù),我們build項目時Databinding會生成多個文件渠抹,我們可以在build文件中查看。
Databinding會將原有的activity_databinding_test.xml進行了拆分毯炮,分別是activity_databinding_test.xml和activity_databinding_test-layout.xml
通過上面的代碼我們發(fā)現(xiàn):Databinding將原有的layout和data標簽去除了逼肯,并為根布局聲明了一個layout/文件名_0的tag,為其它使用到@{}或@={}的控件按順序添加了一個binding_X的tag桃煎。
該配置文件詳細記述了:<Variables name="user" declared="true" type="com.sun.jetpack.databinding.User"> 我們聲明的變量篮幢,變量指向的數(shù)據(jù)類型的絕對路徑。<Target tag="binding_1" view="TextView"> tag對應(yīng)的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發(fā)方法耿战。
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方法又調(diào)用了DataBinderMapperImpl(apt生產(chǎn)的類)中的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個節(jié)點
this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
}
mapBindings調(diào)用了ViewDataBinding類中的mapBindings方法,在加載這個類的時候鸭栖,會先執(zhí)行靜態(tài)塊歌馍,調(diào)用onViewAttachedToWindow,所有沒調(diào)用setUser之前數(shù)據(jù)綁定就是這么做的晕鹊。
問題:沒調(diào)用setUser之前數(shù)據(jù)綁定是怎么做的松却?ViewDataBinding類中有個靜態(tài)塊,調(diào)了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) {
}
};
}
}
靜態(tài)塊中有個OnAttachStateChangeListener()監(jiān)聽事件溅话,當(dāng)控件狀態(tài)發(fā)生改變晓锻,會執(zhí)行一個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的數(shù)組中并返回带射。ViewDataBinding持有Activity或Fragment和View的引用,主要作用一次遍歷View循狰,實例化所有的子View窟社,并存儲到數(shù)組中,解決了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數(shù)組賦值給成員變量,執(zhí)行了invalidateAll()方法程腹,接著執(zhí)行了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)實際上也執(zhí)行了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();
}
}
如果綁定了就不需要再綁定了,如果沒有綁定就執(zhí)行綁定寸潦。
/**
* 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方法對一些狀態(tài)進行了判斷添加回調(diào)色鸳,核心是executeBindings()方法,執(zhí)行綁定操作见转。executeBindings()是一個抽象方法命雀,它在哪實現(xiàn)的呢?我們回到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用于表示哪個屬性發(fā)生變化吏砂,如果是M中的name被修改了 ,dirtyFlags & 0x7L —> 010 & 111 —> 010不為0乘客,讀取user.name,調(diào)用setText設(shè)置值狐血,這是M—>V的過程。當(dāng)(dirtyFlags & 0x4L) != 0成立易核,就是V—>M匈织,它為雙向綁定的控件添加了一個內(nèi)容變化的監(jiān)聽mboundView2androidTextAttrChanged,當(dāng)控件內(nèi)容發(fā)生變化時牡直,就會更新到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上值的變化井氢,當(dāng)我們調(diào)用binding.setUser(user)時會執(zhí)行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調(diào)用create得到WeakPropertyListener,WeakPropertyListener內(nèi)有變量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對應(yīng)的WeakListener,如果為null就調(diào)用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) {
//調(diào)到上面的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)設(shè)置ViewModel的監(jiān)聽事件厌蔽。
三牵辣、小結(jié)
1、DataBinding的關(guān)鍵類
- ActivityDatabindingTestBinding持有綁定類的引用奴饮。
- ActivityDatabindingTestBindingImpl具體實現(xiàn)了綁定纬向。
- BR存儲了VM的id择浊,功能和R文件類似,還用于確定監(jiān)聽器逾条。
- ViewDataBinding持有Activity或者Fragment和View的引用琢岩,主要作用是遍歷View,實例化所有的子View师脂,并存儲在數(shù)組中担孔,省去了findviewbyid的操作。
- DataBinderMapperImpl提供布局文件layoutId到ViewDataBinding類對應(yīng)的映射吃警,主要用于加載layout返回的ViewDataBinding對象
2糕篇、VM變化如何通知到View
設(shè)置監(jiān)聽:updateRegistration—>registerTo—>listener.setTarget(observable)—>target.addOnPropertyChangedCallback(this) 設(shè)置ViewModel的監(jiān)聽事件
VM—>V:notifyPropertyChanged(BR.id)—>mCallbacks.notifyCallbacks—>executeBindings
3、View變化如何同步到VM
設(shè)置監(jiān)聽:requestRebind—>executeBindings—>setTextWatcher設(shè)置對View的監(jiān)聽
V—>VM:回調(diào)InverseBindingListener的onChange設(shè)置值
4酌心、流程圖