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.mzph.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区域内进行代码补充。 输入 一行字符 输出 逆序后的字符串 测试说明 样例输入&…

【LVGL】STM32F429IGT6(在野火官网的LCD例程上)移植LVGL官方的例程(还没写完,有问题 排查中)

这里写目录标题 前言一、本次实验准备1、硬件2、软件 二、移植LVGL代码1、获取LVGL官方源码2、整理一下&#xff0c;下载后的源码文件3、开始移植 三、移植显示驱动1、enable LVGL2、修改报错部分3、修改lv_config4、修改lv_port_disp.c文件到此步遇到的问题 Undefined symbol …

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…

node14升级node16之后,webpack3项目无法启动处理

node从14升级到16之后&#xff0c;项目就无法启动了&#xff0c;研究了webpack3升级5&#xff0c;研究好几个小时都无法启动&#xff0c;最后发现&#xff0c;微微升级几个版本就可以了。webpack还是3 版本改了好多个的&#xff0c;但是不确定具体是哪几个起作用的&#xff0c;…

解读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;为何出现…

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…

SpringData JPA 搭建 xml的 配置方式

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

【C++11】lambda表达式及包装器

一.lambda表达式 1.可调用对象 可调用对象即可以像函数一样被调用的对象&#xff0c;有以下三种&#xff1a; 函数(指针)仿函数对象lambda表达式 tips&#xff1a;调用函数时&#xff0c;既可以用函数名&#xff0c;也可以用函数地址&#xff0c;因为函数名和函数地址是一回事…

Python从入门到精通五:Python数据容器

数据容器入门 为什么学习数据容器 思考一个问题&#xff1a;如果我想要在程序中&#xff0c;记录5名学生的信息&#xff0c;如姓名。 如何做呢&#xff1f; 学习数据容器&#xff0c;就是为了批量存储或批量使用多份数据 Python中的数据容器&#xff1a; 一种可以容纳多份…

Kalman滤波、扩展Kalman滤波、无迹Kalman滤波和异步滤波的原理及其Matlab代码

目录 引言Kalman滤波代码及其结果展示 扩展Kalman滤波代码及其结果展示 无迹Kalman滤波无迹变换无迹Kalman滤波代码及其结果展示 异步无迹Kalman滤波原理代码及其结果展示 引言 本文给出了Kalman Filter&#xff08;卡尔曼滤波&#xff09;、Extended Kalman Filter&#xff0…

leetcode 98. 验证二叉搜索树

leetcode 98. 验证二叉搜索树 题目 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。 节点的右子树只包含 大于 当前节点的数。 所有左子树和右子树自身必须也是…

Softmax回归

一、Softmax回归关键思想 1、回归问题和分类问题的区别 Softmax回归虽然叫“回归”&#xff0c;但是它本质是一个分类问题。回归是估计一个连续值&#xff0c;而分类是预测一个离散类别。 2、Softmax回归模型 Softmax回归跟线性回归一样将输入特征与权重做线性叠加。与线性回归…

Linux安装Nginx并部署Vue项目

今天部署了一个Vue项目到阿里云的云服务器上&#xff0c;现记录该过程。 1. 修改Vue项目配置 我们去项目中发送axios请求的文件里更改一下后端的接口路由&#xff1a; 2. 执行命令打包 npm run build ### 或者 yarn build 打包成功之后&#xff0c;我们会看到一个dist包&a…

[MySQL]SQL优化之索引的使用规则

&#x1f308;键盘敲烂&#xff0c;年薪30万&#x1f308; 目录 一、索引失效 &#x1f4d5;最左前缀法则 &#x1f4d5;范围查询> &#x1f4d5;索引列运算&#xff0c;索引失效 &#x1f4d5;前模糊匹配 &#x1f4d5;or连接的条件 &#x1f4d5;字符串类型不加 …

110. 平衡二叉树(Java)

给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a; 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;t…

如何通过SPI控制Peregrine的数控衰减器

概要 Peregrine的数控衰减器PE4312是6位射频数字步进衰减器(DSA,Digital Step Attenuator)工作频率覆盖1MHz~4GHz,插入损耗2dB左右,衰减步进0.5dB,最大衰减量为31.5dB,高达59dBm的IIP3提供了良好的动态性能,切换时间0.5微秒,供电电源2.3V~5.5V,逻辑控制兼容1.8V,20…