Android--Jetpack--Databinding源碼解析

慢品人間煙火色,閑觀萬事歲月長

一,基本使用

關于databinding的基本使用請看之前的文章

Android--Jetpack--Databinding詳解-CSDN博客

二,xml布局解析

分析源碼呢,主要就是從兩方面入手,一個是使用,一個是APT生成的代碼。

我們看一下上一篇文章我們的布局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 class="MyDataBing"><import type="com.yuanzhen.lifecycledemo.databing.YuanZhen"/><variablename="yuanzhen"type="YuanZhen"/></data><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/txt_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{yuanzhen.name}"android:textSize="40sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/txt_age"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="40sp"android:text="@{String.valueOf(yuanzhen.age)}"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"android:layout_below="@+id/txt_name"/><EditTextandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/txt_age"android:textSize="40sp"android:text="@={yuanzhen.name}"/></RelativeLayout>
</layout>

然后我們看一下APT生成的代碼:

打開之后,格式化一下,看看代碼:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Layout bindingClass="MyDataBing" directory="layout"filePath="app\src\main\res\layout\activity_main.xml" isBindingData="true"isMerge="false" layout="activity_main" modulePackage="com.yuanzhen.lifecycledemo"rootNodeType="android.widget.RelativeLayout"><ClassNameLocation endLine="5" endOffset="26" startLine="5" startOffset="17" /><Variables name="yuanzhen" declared="true" type="YuanZhen"><location endLine="9" endOffset="28" startLine="7" startOffset="8" /></Variables><Imports name="YuanZhen" type="com.yuanzhen.lifecycledemo.databing.YuanZhen"><location endLine="6" endOffset="68" startLine="6" startOffset="8" /></Imports><Targets><Target tag="layout/activity_main_0" view="RelativeLayout"><Expressions /><location endLine="48" endOffset="20" startLine="12" startOffset="4" /></Target><Target id="@+id/txt_name" tag="binding_1" view="TextView"><Expressions><Expression attribute="android:text" text="yuanzhen.name"><Location endLine="21" endOffset="42" startLine="21" startOffset="12" /><TwoWay>false</TwoWay><ValueLocation endLine="21" endOffset="40" startLine="21" startOffset="28" /></Expression></Expressions><location endLine="26" endOffset="55" startLine="17" startOffset="8" /></Target><Target id="@+id/txt_age" tag="binding_2" view="TextView"><Expressions><Expression attribute="android:text" text="String.valueOf(yuanzhen.age)"><Location endLine="33" endOffset="57" startLine="33" startOffset="12" /><TwoWay>false</TwoWay><ValueLocation endLine="33" endOffset="55" startLine="33" startOffset="28" /></Expression></Expressions><location endLine="38" endOffset="49" startLine="28" startOffset="8" /></Target><Target tag="binding_3" view="EditText"><Expressions><Expression attribute="android:text" text="yuanzhen.name"><Location endLine="44" endOffset="43" startLine="44" startOffset="12" /><TwoWay>true</TwoWay><ValueLocation endLine="44" endOffset="41" startLine="44" startOffset="29" /></Expression></Expressions><location endLine="44" endOffset="45" startLine="39" startOffset="8" /></Target></Targets>
</Layout>

我們發現每個view都包裝了一個Targets標簽,對應了一個tag。

比如RelativeLayout對應的是layout/activity_main_0

然后看看我們的textview:

<Target id="@+id/txt_name" tag="binding_1" view="TextView"><Expressions><Expression attribute="android:text" text="yuanzhen.name"><Location endLine="21" endOffset="42" startLine="21" startOffset="12" /><TwoWay>false</TwoWay><ValueLocation endLine="21" endOffset="40" startLine="21" startOffset="28" /></Expression></Expressions><location endLine="26" endOffset="55" startLine="17" startOffset="8" />
</Target>

?通過text將yuanzhen.name賦值給了textview

然后在打開另一個APT生成的文件:

看看它的代碼:

<?xml version="1.0" encoding="utf-8"?><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"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"><TextViewandroid:id="@+id/txt_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:tag="binding_1"        android:textSize="40sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/txt_age"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="40sp"android:tag="binding_2"                       app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"android:layout_below="@+id/txt_name"/><EditTextandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/txt_age"android:textSize="40sp"android:tag="binding_3"         /></RelativeLayout>

上面空了一大塊,把布局文件的layout去掉了,其余的和我們的布局文件一樣,除此之外,它給每個view都增加了一個tag,這個tag與上面的tag一一對應,這就是布局的解析。

三,xml代碼解析

首先我們看一下上一章我們的使用代碼:

public class MainActivity extends AppCompatActivity {private MyDataBing dataBinding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);YuanZhen yuanZhen =new YuanZhen("袁震",18);dataBinding.setYuanzhen(yuanZhen);new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {Thread.sleep(1000);yuanZhen.setName(yuanZhen.getName()+i);yuanZhen.setAge(18+i);} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}}

首先,我們看一下dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)這句代碼的實現,它會走到DataBindingUtil的setContentView():

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(layoutId);View decorView = activity.getWindow().getDecorView();ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}

前三行代碼我們應該都能看懂

獲取到了conetntView,并傳入了方法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) {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);}
}

該方法會調用bind(component, children, layoutId)方法:

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

然后會調用??sMapper.getDataBinder(bindingComponent, root, layoutId):

public abstract class DataBinderMapper {public abstract ViewDataBinding getDataBinder(DataBindingComponent bindingComponent, View view,int layoutId);public abstract ViewDataBinding getDataBinder(DataBindingComponent bindingComponent,View[] view, int layoutId);public abstract int getLayoutId(String tag);public abstract String convertBrIdToString(int id);@NonNullpublic List<DataBinderMapper> collectDependencies() {// default implementation for backwards compatibility.return Collections.emptyList();}
}

然后查看getDataBinder的引用,會找到APT生成的DataBinderMapperImpl類的getDataBinder方法:

public class DataBinderMapperImpl extends DataBinderMapper {。。。@Overridepublic 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: {if ("layout/activity_main_0".equals(tag)) {return new MyDataBingImpl(component, view);}throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);}}}return null;
}
。。。}

這里比較關鍵的代碼是:如果我們傳過來的tag是layout/activity_main_0,就是前面生成的xml里面的根節點的tag,那么就創建我們自定義命名的MyDataBingImpl。

然后我們查看MyDataBingImpl的構造函數:

public MyDataBingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {this(bindingComponent, root, mapBindings(bindingComponent, root, 4, sIncludes, sViewsWithIds));
}

會發現這里有個4,4的意思就是xml文件里面的4個view節點。

繼續往下看mapBindings源碼:

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;
}

?創建了一個大小為4的對象數組,然后調用mapBindings:

private static void mapBindings(DataBindingComponent bindingComponent, View view,Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,boolean isRoot) {final int indexInIncludes;final ViewDataBinding existingBinding = getBinding(view);if (existingBinding != null) {return;}Object objTag = view.getTag();final String tag = (objTag instanceof String) ? (String) objTag : null;boolean isBound = false;if (isRoot && tag != null && tag.startsWith("layout")) {final int underscoreIndex = tag.lastIndexOf('_');if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) {final int index = parseTagInt(tag, underscoreIndex + 1);if (bindings[index] == null) {bindings[index] = view;}indexInIncludes = includes == null ? -1 : index;isBound = true;} else {indexInIncludes = -1;}} else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {int tagIndex = parseTagInt(tag, BINDING_NUMBER_START);if (bindings[tagIndex] == null) {bindings[tagIndex] = view;}isBound = true;indexInIncludes = includes == null ? -1 : tagIndex;} else {// Not a bound viewindexInIncludes = -1;}if (!isBound) {final int id = view.getId();if (id > 0) {int index;if (viewsWithIds != null && (index = viewsWithIds.get(id, -1)) >= 0 &&bindings[index] == null) {bindings[index] = view;}}}if (view instanceof  ViewGroup) {final ViewGroup viewGroup = (ViewGroup) view;final int count = viewGroup.getChildCount();int minInclude = 0;for (int i = 0; i < count; i++) {final View child = viewGroup.getChildAt(i);boolean isInclude = false;if (indexInIncludes >= 0 && child.getTag() instanceof String) {String childTag = (String) child.getTag();if (childTag.endsWith("_0") &&childTag.startsWith("layout") && childTag.indexOf('/') > 0) {// This *could* be an include. Test against the expected includes.int includeIndex = findIncludeIndex(childTag, minInclude,includes, indexInIncludes);if (includeIndex >= 0) {isInclude = true;minInclude = includeIndex + 1;final int index = includes.indexes[indexInIncludes][includeIndex];final int layoutId = includes.layoutIds[indexInIncludes][includeIndex];int lastMatchingIndex = findLastMatching(viewGroup, i);if (lastMatchingIndex == i) {bindings[index] = DataBindingUtil.bind(bindingComponent, child,layoutId);} else {final int includeCount =  lastMatchingIndex - i + 1;final View[] included = new View[includeCount];for (int j = 0; j < includeCount; j++) {included[j] = viewGroup.getChildAt(i + j);}bindings[index] = DataBindingUtil.bind(bindingComponent, included,layoutId);i += includeCount - 1;}}}}if (!isInclude) {mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);}}}
}

這段源碼主要是通過tag解析出view,放到數組里面。

這樣我們在使用的時候,就能拿到xml里面的view了:

這樣xml的代碼解析就完成了。

四,核心原理

databinding的核心邏輯就是更新數據的同時自動更新UI。那么我們可以判斷出它的整體實際上肯定是一個觀察者模式。首先被觀察者肯定是數據類,就是我們的YuanZhen這個類:

public class YuanZhen extends BaseObservable {public YuanZhen(String name, int age) {this.name = name;this.age = age;}private String name;private int age;public void setName(String name) {this.name = name;notifyPropertyChanged(BR.name);}public void setAge(int age) {this.age = age;notifyPropertyChanged(BR.age);}@Bindablepublic String getName() {return name;}@Bindablepublic int getAge() {return age;}
}

它繼承了BaseObservable并且在數據發生變化的時候調用了notify方法,通知觀察者去更新UI。

?所以觀察者肯定是更新ui的類,就是我們APT生成的MyDataBingImpl這個類。

中間肯定會有注冊和反注冊觀察者的邏輯。下面我們還是先從使用入手分析:

我們來看下這句代碼:dataBinding.setYuanzhen(yuanZhen) ,它是初始化的代碼,直接點擊是找不到的,它是在APT生成的MyDataBingImpl類里面:

@SuppressWarnings("unchecked")
public class MyDataBingImpl extends MyDataBing  {。。。。public void setYuanzhen(@Nullable com.yuanzhen.lifecycledemo.databing.YuanZhen Yuanzhen) {updateRegistration(0, Yuanzhen);this.mYuanzhen = Yuanzhen;synchronized(this) {mDirtyFlags |= 0x1L;}notifyPropertyChanged(BR.yuanzhen);super.requestRebind();}}

先來看updateRegistration(0, Yuanzhen):

protected boolean updateRegistration(int localFieldId, Observable observable) {return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}

先看看第三個參數:?CREATE_PROPERTY_LISTENER,從名字可以看出,它是創建Property監聽器的,看看它的源碼:

private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {@Overridepublic WeakListener create(ViewDataBinding viewDataBinding,int localFieldId,ReferenceQueue<ViewDataBinding> referenceQueue) {return new WeakPropertyListener(viewDataBinding, localFieldId, referenceQueue).getListener();}
};

果然是創建了一個WeakPropertyListener,看看它的源碼:

private static class WeakPropertyListener extends Observable.OnPropertyChangedCallbackimplements ObservableReference<Observable> {final WeakListener<Observable> mListener;public WeakPropertyListener(ViewDataBinding binder,int localFieldId,ReferenceQueue<ViewDataBinding> referenceQueue) {mListener = new WeakListener<Observable>(binder, localFieldId, this, referenceQueue);}@Overridepublic WeakListener<Observable> getListener() {return mListener;}@Overridepublic void addListener(Observable target) {target.addOnPropertyChangedCallback(this);}@Overridepublic void removeListener(Observable target) {target.removeOnPropertyChangedCallback(this);}@Overridepublic void setLifecycleOwner(LifecycleOwner lifecycleOwner) {}@Overridepublic 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);}
}

從源碼大致可以看出,這個監聽器內部創建了WeakListener? ?并且將WeakListenter和?ViewDataBinding,localFieldId進行綁定,并在調用onPropertyChanged方法時,調用ViewDataBinding的handleFieldChange方法,并將WeakListenter的mLocalFieldId傳給后者。

然后看下WeakListener? ?的源碼:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
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,ReferenceQueue<ViewDataBinding> referenceQueue) {super(binder, referenceQueue);mLocalFieldId = localFieldId;mObservable = observable;}public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {mObservable.setLifecycleOwner(lifecycleOwner);}public void setTarget(T object) {unregister();mTarget = object;if (mTarget != null) {mObservable.addListener(mTarget);}}public boolean unregister() {boolean unregistered = false;if (mTarget != null) {mObservable.removeListener(mTarget);unregistered = true;}mTarget = null;return unregistered;}public T getTarget() {return mTarget;}@Nullableprotected ViewDataBinding getBinder() {ViewDataBinding binder = get();if (binder == null) {unregister(); // The binder is dead}return binder;}
}

?在WeakListener? ?里面主要是將ViewDataBinding和localFieldId進行了綁定

然后我們再繼續看updateRegistration的源碼:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
protected 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]:看看它是什么時候創建的
private WeakListener[] mLocalFieldObservers;
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() {@Overridepublic void doFrame(long frameTimeNanos) {mRebindRunnable.run();}};} else {mFrameCallback = null;mUIThreadHandler = new Handler(Looper.myLooper());}
}

原來是在我們上面講的xml代碼解析里面初始化ViewDataBinding的時候創建的:

public MyDataBingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {this(bindingComponent, root, mapBindings(bindingComponent, root, 4, sIncludes, sViewsWithIds));
}

并且數組的大小是4。這個4是怎么來的呢?

繼續返回MyDataBingImpl的setYuanzhen方法往下看:

notifyPropertyChanged(BR.yuanzhen);

看一下BR這個類,也是APT生成的:

public class BR {public static final int _all = 0;public static final int age = 1;public static final int name = 2;public static final int yuanzhen = 3;
}

主要就是將屬性用數字定義了。數組的大小 就是這個類的屬性的數量。

再返回頭來看updateRegistration:如果WeakLister不存在,就走registerTo(localFieldId, observable, listenerCreator);方法:

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, sReferenceQueue);mLocalFieldObservers[localFieldId] = listener;if (mLifecycleOwner != null) {listener.setLifecycleOwner(mLifecycleOwner);}}listener.setTarget(observable);
}

這個方法主要是通過上面創建的CreateWeakListener來創建WeakListener,并將WeakListener放入mLocalFieldObservers數組中它對應的位置。

看完了初始化綁定關系,再來看看被觀察者BaseObservable:

public class BaseObservable implements Observable {private transient PropertyChangeRegistry mCallbacks;public BaseObservable() {}@Overridepublic void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {synchronized (this) {if (mCallbacks == null) {mCallbacks = new PropertyChangeRegistry();}}mCallbacks.add(callback);}@Overridepublic void removeOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {synchronized (this) {if (mCallbacks == null) {return;}}mCallbacks.remove(callback);}/*** Notifies listeners that all properties of this instance have changed.*/public void notifyChange() {synchronized (this) {if (mCallbacks == null) {return;}}mCallbacks.notifyCallbacks(this, 0, null);}/*** Notifies listeners that a specific property has changed. The getter for the property* that changes should be marked with {@link Bindable} to generate a field in* <code>BR</code> to be used as <code>fieldId</code>.** @param fieldId The generated BR id for the Bindable field.*/public void notifyPropertyChanged(int fieldId) {synchronized (this) {if (mCallbacks == null) {return;}}mCallbacks.notifyCallbacks(this, fieldId, null);}
}

首先看mCallbacks的源碼:

public class PropertyChangeRegistry extendsCallbackRegistry<Observable.OnPropertyChangedCallback, Observable, Void> {private static final CallbackRegistry.NotifierCallback<Observable.OnPropertyChangedCallback, Observable, Void> NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback<Observable.OnPropertyChangedCallback, Observable, Void>() {@Overridepublic void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observable sender,int arg, Void notUsed) {callback.onPropertyChanged(sender, arg);}};public PropertyChangeRegistry() {super(NOTIFIER_CALLBACK);}/*** Notifies registered callbacks that a specific property has changed.** @param observable The Observable that has changed.* @param propertyId The BR id of the property that has changed or BR._all if the entire*                   Observable has changed.*/public void notifyChange(@NonNull Observable observable, int propertyId) {notifyCallbacks(observable, propertyId, null);}
}

可以看出它主要是用來通知觀察者更新消息的。

我們調用notifyPropertyChanged就會調用PropertyChangeRegistry的notifyChange方法。

然后依次往下調用:

public synchronized void notifyCallbacks(T sender, int arg, A arg2) {mNotificationLevel++;notifyRecurse(sender, arg, arg2);mNotificationLevel--;if (mNotificationLevel == 0) {if (mRemainderRemoved != null) {for (int i = mRemainderRemoved.length - 1; i >= 0; i--) {final long removedBits = mRemainderRemoved[i];if (removedBits != 0) {removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits);mRemainderRemoved[i] = 0;}}}if (mFirst64Removed != 0) {removeRemovedCallbacks(0, mFirst64Removed);mFirst64Removed = 0;}}
}

之后notifyRecurse

private void notifyRecurse(T sender, int arg, A arg2) {final int callbackCount = mCallbacks.size();final int remainderIndex = mRemainderRemoved == null ? -1 : mRemainderRemoved.length - 1;// Now we've got all callbakcs that have no mRemainderRemoved value, so notify the// others.notifyRemainder(sender, arg, arg2, remainderIndex);// notifyRemainder notifies all at maxIndex, so we'd normally start at maxIndex + 1// However, we must also keep track of those in mFirst64Removed, so we add 2 instead:final int startCallbackIndex = (remainderIndex + 2) * Long.SIZE;// The remaining have no bit setnotifyCallbacks(sender, arg, arg2, startCallbackIndex, callbackCount, 0);
}

然后:

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.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);}bitMask <<= 1;}
}

然后看看NotifierCallback

public abstract static class NotifierCallback<C, T, A> {/*** Called by CallbackRegistry during* {@link CallbackRegistry#notifyCallbacks(Object, int, Object)}} to notify the callback.** @param callback The callback to notify.* @param sender The opaque sender object.* @param arg The opaque notification parameter.* @param arg2 An opaque argument passed in*        {@link CallbackRegistry#notifyCallbacks}* @see CallbackRegistry#CallbackRegistry(CallbackRegistry.NotifierCallback)*/public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2);
}

之后又回到了PropertyChangeRegistry的回調里面:

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>() {public void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observable sender, int arg, Void notUsed) {callback.onPropertyChanged(sender, arg);}};public PropertyChangeRegistry() {super(NOTIFIER_CALLBACK);}public void notifyChange(@NonNull Observable observable, int propertyId) {this.notifyCallbacks(observable, propertyId, (Object)null);}
}

?然后繼續往下走onPropertyChanged

public interface Observable {/*** Adds a callback to listen for changes to the Observable.* @param callback The callback to start listening.*/void addOnPropertyChangedCallback(OnPropertyChangedCallback callback);/*** Removes a callback from those listening for changes.* @param callback The callback that should stop listening.*/void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback);/*** The callback that is called by Observable when an observable property has changed.*/abstract class OnPropertyChangedCallback {/*** Called by an Observable whenever an observable property changes.* @param sender The Observable that is changing.* @param propertyId The BR identifier of the property that has changed. The getter*                   for this property should be annotated with {@link Bindable}.*/public abstract void onPropertyChanged(Observable sender, int propertyId);}
}

?

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

繼續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);}}
}

然后看看mRebindRunnable里面:

private final Runnable mRebindRunnable = new Runnable() {@Overridepublic 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 IntelliJif (!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();}
};

之后executePendingBindings

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 mPendingHaltedif (mRebindHalted) {mRebindCallbacks.notifyCallbacks(this, HALTED, null);}}if (!mRebindHalted) {executeBindings();if (mRebindCallbacks != null) {mRebindCallbacks.notifyCallbacks(this, REBOUND, null);}}mIsExecutingPendingBindings = false;
}

看看executeBindings():

protected abstract void executeBindings();

然后在MyDataBingImpl里面找到它的實現:

    @Overrideprotected void executeBindings() {long dirtyFlags = 0;synchronized(this) {dirtyFlags = mDirtyFlags;mDirtyFlags = 0;}java.lang.String yuanzhenName = null;com.yuanzhen.lifecycledemo.databing.YuanZhen yuanzhen = mYuanzhen;int yuanzhenAge = 0;java.lang.String stringValueOfYuanzhenAge = null;if ((dirtyFlags & 0xfL) != 0) {if ((dirtyFlags & 0xbL) != 0) {if (yuanzhen != null) {// read yuanzhen.nameyuanzhenName = yuanzhen.getName();}}if ((dirtyFlags & 0xdL) != 0) {if (yuanzhen != null) {// read yuanzhen.ageyuanzhenAge = yuanzhen.getAge();}// read String.valueOf(yuanzhen.age)stringValueOfYuanzhenAge = java.lang.String.valueOf(yuanzhenAge);}}// batch finishedif ((dirtyFlags & 0xbL) != 0) {// api target 1androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView3, yuanzhenName);androidx.databinding.adapters.TextViewBindingAdapter.setText(this.txtName, yuanzhenName);}if ((dirtyFlags & 0x8L) != 0) {// api target 1androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView3, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, mboundView3androidTextAttrChanged);}if ((dirtyFlags & 0xdL) != 0) {// api target 1androidx.databinding.adapters.TextViewBindingAdapter.setText(this.txtAge, stringValueOfYuanzhenAge);}}

看到上面的最終數據的改變也是通過調用setText等方法來實現的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/214508.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/214508.shtml
英文地址,請注明出處:http://en.pswp.cn/news/214508.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

STM32F407-14.1.0-01高級定時器簡介

TIM1 和 TIM8 簡介 高級控制定時器&#xff08;TIM1 和 TIM8&#xff09;包含一個 16 位自動重載計數器&#xff0c;該計數器由可編程預分頻器驅動。 此類定時器可用于各種用途&#xff0c;包括測量輸入信號的脈沖寬度&#xff08;輸入捕獲&#xff09;&#xff0c;或者生成輸出…

微軟NativeApi-NtQuerySystemInformation

微軟有一個比較實用的Native接口&#xff1a;NtQuerySystemInformation&#xff0c;具體可以參考微軟msdn官方文檔&#xff1a;NtQuerySystemInformation&#xff0c; 是一個系統函數&#xff0c;用于收集特定于所提供的指定種類的系統信息。ProcessHacker等工具使用NtQuerySys…

Javascript 數組array賦值與取值

Javascript 數組array賦值與取值 目錄 Javascript 數組array賦值與取值 一、數組元素的賦值 1、在創建Array對象時直接賦值 2、利用Array對象的元素下標對數組進行賦值 二、數組元素的獲取 一、數組元素的賦值 對數組元素賦值共有2種方法&#xff1a; &#xff08;1&am…

每日一題,頭歌平臺c語言題目

任務描述 題目描述:輸入一個字符串&#xff0c;輸出反序后的字符串。 相關知識&#xff08;略&#xff09; 編程要求 請仔細閱讀右側代碼&#xff0c;結合相關知識&#xff0c;在Begin-End區域內進行代碼補充。 輸入 一行字符 輸出 逆序后的字符串 測試說明 樣例輸入&…

項目實戰第四十七講:易寶支付對接詳解(保姆級教程)

易寶支付對接(保姆級教程) 為了實現項目的支付需求,公司選擇了易寶支付進行對接,本文是項目實戰第四十七講,詳解易寶支付對接。 文章目錄 易寶支付對接(保姆級教程)1、需求背景2、流程圖3、技術方案4、相關接口4.1、入駐相關(商戶入網)4.2、賬戶相關接口(充值、提現、…

【LVGL】STM32F429IGT6(在野火官網的LCD例程上)移植LVGL官方的例程(還沒寫完,有問題 排查中)

這里寫目錄標題 前言一、本次實驗準備1、硬件2、軟件 二、移植LVGL代碼1、獲取LVGL官方源碼2、整理一下&#xff0c;下載后的源碼文件3、開始移植 三、移植顯示驅動1、enable LVGL2、修改報錯部分3、修改lv_config4、修改lv_port_disp.c文件到此步遇到的問題 Undefined symbol …

Vue路由守衛筆記

路由守衛 當路由切換時&#xff0c;判斷權限 路由守衛類型 1.全局守衛 2.獨享守衛 3.組件內守衛 1.全局守衛 1.前置路由守衛 全局前置路由守衛————初始化的時候被調用、每次路由切換之前被調用 在需要加上路由守衛的路由配置中加上 meta&#xff1a;{isAuth&#xff1…

x的平方根算法(leetcode第69題)

題目描述&#xff1a; 給你一個非負整數 x &#xff0c;計算并返回 x 的 算術平方根 。由于返回類型是整數&#xff0c;結果只保留 整數部分 &#xff0c;小數部分將被 舍去 。注意&#xff1a;不允許使用任何內置指數函數和算符&#xff0c;例如 pow(x, 0.5) 或者 x ** 0.5 。…

react中img引入本地圖片的方式

在html文件中,可以直接<img src=./roadBook.png /> 但是在jsx文件中,不支持這種寫法 必須這樣寫 在css樣式中 App.css .img{background: url(./img/roadBook.png) }App.js import ./App.css;<div className=img></div> 1.基于es6Module //導入 import…

【PTA-C語言】實驗五-一維數組

如果代碼存在問題&#xff0c;麻煩大家指正 ~ ~有幫助麻煩點個贊 ~ ~ 實驗五-一維數組 7-1 查找整數&#xff08;分數 15&#xff09;7-2 交換最小值和最大值&#xff08;分數 15&#xff09;7-3 簡化的插入排序&#xff08;分數 15&#xff09;7-4 刪除指定數據&#xff08;分…

node14升級node16之后,webpack3項目無法啟動處理

node從14升級到16之后&#xff0c;項目就無法啟動了&#xff0c;研究了webpack3升級5&#xff0c;研究好幾個小時都無法啟動&#xff0c;最后發現&#xff0c;微微升級幾個版本就可以了。webpack還是3 版本改了好多個的&#xff0c;但是不確定具體是哪幾個起作用的&#xff0c;…

var、let、const 的區別?

var 1、var 聲明的變量在全局內有效 2、可以重復聲明 3、var 聲明的變量存在變量提升 let 1、遇到{}可開啟塊級作用域 2、不能重復聲明--- 可以防止變量重復定義產生的沖突&#xff0c;會直接報錯 3、let 聲明的變量不存在變量提升 const 1、const 聲明…

解讀unity內置的軟陰影處理方式

解讀unity內置的軟陰影處理方式&#xff1a; 參考網址&#xff1a; https://blog.csdn.net/cgy56191948/article/details/105726682 https://blog.csdn.net/weixin_45776473/article/details/119582218 https://tajourney.games/5482/ 上面的博客已經論述了&#xff0c;為何出現…

個人博客搭建保姆級教程-Nginx篇

官方文檔 nginx documentation 說明 nginx是我們本次教程使用的http服務器。它能承受很高的并發&#xff0c;并且安裝簡單&#xff0c;占用內存少。 在服務器篇我們提到了nginx的安裝&#xff0c;在發布篇我們簡述了該怎么放置我們創建的博客html文檔。 在本篇&#xff0c…

css的復合選擇器(有案例)

目錄 復合選擇器的描述 后代選擇器&#xff08;常用重點&#xff09; 子選擇器 并集選擇器&#xff08;重點常用&#xff09; 偽類選擇器 鏈接偽類選擇器 focus 偽類選擇器 知識總結&#xff1a; 案例實現&#xff1a; 復合選擇器的描述 在 CSS 中&#xff0c;可以根…

日志門面slf4j和各日志框架

簡介 簡單日志門面(Simple Logging Facade For Java) SLF4J主要是為了給Java日志訪問提供一套標準、規范的API框架&#xff0c; 其主要意義在于提供接口&#xff0c;具體的實現可以交由其他日志框架&#xff0c;如log4j、logback、log4j2。 對于一般的Java項目而言&#xff…

一個用于處理嵌入式系統中的 NAND Flash 存儲器的工具 `kobs-ng`

一個用于處理嵌入式系統中的 NAND Flash 存儲器的工具 kobs-ng kobs-ng 是一個用于處理嵌入式系統中的 NAND Flash 存儲器的工具。它是 U-Boot&#xff08;開源引導加載程序&#xff09;中的一個子項目&#xff0c;用于擦除、寫入和讀取 NAND Flash 設備上的數據。 以下是 kob…

SpringData JPA 搭建 xml的 配置方式

1.導入版本管理依賴 到父項目里 <dependencyManagement><dependencies><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-bom</artifactId><version>2021.1.10</version><scope>…

【力扣100】238.除自身以外數組的乘積

添加鏈接描述 class Solution:def productExceptSelf(self, nums: List[int]) -> List[int]:# 構造第i個數的左右數組n len(nums)left,right,res [1]*n,[1]*n,[1]*nfor i in range(1,n):left[i] nums[i-1]*left[i-1]for i in range(n-2,-1,-1):right[i] nums[i1]*right…

STM32Cube高效開發教程<基礎篇>(十二)----ADC

聲明:本人水平有限,博客可能存在部分錯誤的地方,請廣大讀者諒解并向本人反饋錯誤。 ?? 本專欄博客參考《STM32Cube高效開發教程(基礎篇)》,有意向的讀者可以購買正版書籍輔助學習,本書籍由王維波老師、鄢志丹老師、王釗老師傾力打造,書籍內容干貨滿滿。 一、功能概述 …