layout布局在编译过程生成的重要文件(以activity_main.xml为例):
1、app\build\intermediates\data_binding_layout_info_type_package目录的activity_main-layout.xml,主要处理数据部分,记录节点信息,生成tag,twoway是记录是否双向绑定,截取部分代码
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Layout directory="layout" filePath="app\src\main\res\layout\activity_main.xml"
isBindingData="true" isMerge="false" layout="activity_second"
modulePackage="com.example.myapplication" rootNodeType="android.widget.LinearLayout">
<Variables name="user" declared="true" type="com.example.myapplication.User">
<location endLine="8" endOffset="51" startLine="6" startOffset="8" />
</Variables>
<Targets>
<Target tag="layout/activity_main_0" view="LinearLayout">
<Expressions />
<location endLine="28" endOffset="18" startLine="12" startOffset="4" />
</Target>
<Target id="@+id/idName" tag="binding_1" view="TextView">
<Expressions>
<Expression attribute="android:text" text="user.name">
<Location endLine="21" endOffset="38" startLine="21" startOffset="12" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="21" endOffset="36" startLine="21" startOffset="28" />
</Expression>
</Expressions>
<location endLine="21" endOffset="40" startLine="17" startOffset="8" />
</Target>
</Targets>
</Layout>
2、appbuild\generated\data_binding_base_class_source_out下的ActivityMainBinding.java,View和Model的实例
// Generated by data binding compiler. Do not edit!
package com.example.myapplication.databinding;
public abstract class ActivityMainBinding extends ViewDataBinding {
...省略部分
@NonNull
public final TextView idName;
@Bindable
protected User mUser;
protected ActivityMainBinding(Object _bindingComponent, View _root,
int _localFieldCount, LinearLayout llId, NavigatorBar navigatorBar,
TextView idName ) {
super(_bindingComponent, _root, _localFieldCount);
this.llId = llId;
this.navigatorBar = navigatorBar;
this.idName = idName;
}
public abstract void setUser(
@Nullable User user);
@Nullable
public User getUser() {
return mUser;
}
@NonNull
public static ActivityMainBinding inflate(
@NonNull LayoutInflater inflater, @Nullable ViewGroup root, boolean attachToRoot) {
return inflate(inflater, root, attachToRoot, DataBindingUtil.getDefaultComponent());
}
/**
* 省略一些 @Deprecated 方法
*/
@NonNull
public static ActivityMainBinding inflate(
@NonNull LayoutInflater inflater) {
return inflate(inflater, DataBindingUtil.getDefaultComponent());
}
public static ActivityMainBinding bind(@NonNull View view) {
return bind(view, DataBindingUtil.getDefaultComponent());
}
}
3、app\build\generated\ap_generated_sources下的BR.java。当数据改变后,可以用该标识符通知 DataBinding,用新的数据去更新UI。
public class BR {
public static int _all = 0;
public static int user = 1;
}
下面分析一下源代码
通过layout布局映射获取相应的Binding。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
}
}
进入DataBindingUtil中看一下 ,发现DataBindingUtil.setContentView其实落脚点也是activity.setContentView。(针对自定义view,也提供了inflate方法,可以去设置layout)。代码按调用顺序调整了一下,
/**
* Utility class to create {@link ViewDataBinding} from layouts.
*/
public class DataBindingUtil {
private static DataBinderMapper sMapper = new DataBinderMapperImpl();
private static DataBindingComponent sDefaultComponent = null;
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
activity.setContentView(layoutId);
//各类布局源码最终根节点id都是android.R.id.content,所以这里是获取根节点id的view
View decorView = activity.getWindow().getDecorView();
ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
return 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) {
//如果仅有一个子View
final View childView = parent.getChildAt(endChildren - 1);
return bind(component, childView, layoutId);
} else {
//如果有duo个子View
final View[] children = new View[childrenAdded];
for (int i = 0; i < childrenAdded; i++) {
children[i] = parent.getChildAt(i + startChildren);
}
return bind(component, children, layoutId);
}
}
}
进入DataBindingUtil类中的bind方法看一下
@SuppressWarnings("unchecked")
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View[] roots,
int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);
}
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}
关注到sMapper。是个静态成员变量,也就是类被调用时便开始初始化了
public class DataBindingUtil {
private static DataBinderMapper sMapper = new DataBinderMapperImpl();
private static DataBindingComponent sDefaultComponent = null;
}
/**
*这个类在编译时动态生成的,看到里面还有一个DataBinderMapperImpl,待会儿说一下
*/
public class DataBinderMapperImpl extends MergedDataBinderMapper {
DataBinderMapperImpl() {
//调用的是父类的addMapper方法
//注意这里添加的并不是自身,而是下面要说的另一个同名的实现类
addMapper(new com.example.myapplication.DataBinderMapperImpl());
}
}
public class MergedDataBinderMapper extends DataBinderMapper
public void addMapper(DataBinderMapper mapper) {
Class<? extends DataBinderMapper> mapperClass = mapper.getClass();
// 如果之前不存在,则加入 mExistingMappers
if (mExistingMappers.add(mapperClass)) {
mMappers.add(mapper);
//获取该 mapper 所依赖的其它 DataBinderMapper,也加进去
final List<DataBinderMapper> dependencies = mapper.collectDependencies();
for(DataBinderMapper dependency : dependencies) {
//将依赖的其他DataBinderMapper也添加进去
addMapper(dependency);
}
}
}
}
addMaper方法是将生成的DataBinderMapperImpl以及它所依赖的其他DataBinderMapper加入到mExistingMappers和mMappers集合中。下面进入com.example.myapplication包下的DataBinderMapperImpl的getDataBinder看看
package com.example.myapplication;
public class DataBinderMapperImpl extends DataBinderMapper {
private static final int LAYOUT_ACTIVITYSTARTDETAIL = 1;
private static final int LAYOUT_ACTIVITYMAIN = 2;
private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(2);
static {
//将databinding的布局文件和类定义的常量对应
INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.myapplication.R.layout.activity_start_detail, LAYOUT_ACTIVITYSTARTDETAIL);
INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.myapplication.R.layout.activity_main, LAYOUT_ACTIVITYMAIN);
}
@Override
public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
//根据layout布局文件的id获取自定义的id
int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
if(localizedLayoutId > 0) {
//获取文件的tag,前面有提到编译时自动生成的tag
final Object tag = view.getTag();
if(tag == null) {
throw new RuntimeException("view must have a tag");
}
//根据tag加载对应的bindingImpl
switch(localizedLayoutId) {
case LAYOUT_ACTIVITYSTARTDETAIL: {
if ("layout/activity_start_detail_0".equals(tag)) {
return new ActivityStartDetailBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_start_detail is invalid. Received: " + tag);
}
case LAYOUT_ACTIVITYMAIN: {
if ("layout/activity_main_0".equals(tag)) {
return new ActivityMainBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
}
}
}
return null;
}
@Override
public List<DataBinderMapper> collectDependencies() {
ArrayList<DataBinderMapper> result = new ArrayList<DataBinderMapper>(8);
result.add(new androidx.databinding.library.baseAdapters.DataBinderMapperImpl());
return result;
}
}
所以DataBindingUtil.setContentView方法最后返回的是根据layout文件在编译期间生成的对应的ViewBinding的实现类(比如activity_main.xml对应的ActivityMainBindingImpl),看下这个类进入构造方法
public class ActivityMainBindingImpl extends ActivityMainBinding {
@Nullable
private static final androidx.databinding.ViewDataBinding.IncludedLayouts sIncludes;
@Nullable
private static final android.util.SparseIntArray sViewsWithIds;
static {
sIncludes = null;
sViewsWithIds = new android.util.SparseIntArray();
...
sViewsWithIds.put(R.id.idName, 5);
}
public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
this(bindingComponent, root, mapBindings(bindingComponent, root, 6, sIncludes, sViewsWithIds));
}
private ActivityStartDetailBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(XXXXX);
//将tag置空
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x1L;
}
requestRebind();
}
}
第一个构造方法中调用了父类ViewDataBinding的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")) {
// 判断是否是根布局, 根布局的 tag = layout/activity_xxx_数字
final int underscoreIndex = tag.lastIndexOf('_');
if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) {
//以tag后的数字为下标,将对应tag的View放进指定下标的数组位置中
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)) {
// 判断 tag 是否以 binding_ 开头
//以tag后的数字为下标,将对应tag的View放进指定下标的数组位置中
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 view
indexInIncludes = -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++) {
// 遍历 ViewGroup 的 children
final View child = viewGroup.getChildAt(i);
//是否为include布局的标记
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) {
//对include布局 进行处理
// 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);
}
}
}
}
我们可以知道mapBindings方法返回bindings数组,数组中存放的就是布局文件中的View,而数组下标以生成的tag中的数字作为View在bindings数组中对应的下标。现在可以回到构造方法里面往下看。
public ActivityStartDetailBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
this(bindingComponent, root, mapBindings(bindingComponent, root, 6, sIncludes, sViewsWithIds));
}
private ActivityStartDetailBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 0
, (android.widget.Button) bindings[5]
, (android.widget.FrameLayout) bindings[3]
, (com.sf.trtms.lib.widget.NavigatorBar) bindings[1]
, (androidx.recyclerview.widget.RecyclerView) bindings[4]
, (android.view.View) bindings[2]
);
//将tag置空
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
ActivityStartDetailBindingImpl调用了ActivityStartDetailBinding的构造方法,ActivityStartDetailBinding的构造方法又调用了ViewDataBinding的构造方法。
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) {
//设备系统 API不低于16
//进行mChoreographer初始化以及mFrameCallback的具体实现
mChoreographer = Choreographer.getInstance();
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
mRebindRunnable.run();
}
};
} else {
//绑定至主线程的Handler
mFrameCallback = null;
mUIThreadHandler = new Handler(Looper.myLooper());
}
}
(Choreographer是UI渲染的重要类,后面在性能优化里面加一篇讲解一下)。无论api为多少,当收到消息时最终都会执行mRebindRunnable.run();这是后话,我们回到ActivityStartDetailBindingImpl的构造方法继续往下走invalidateAll()方法。
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x8L;
}
requestRebind();
}
/** ViewDataBinding的方法**/
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
final LifecycleOwner owner = this.mLifecycleOwner;
if (owner != null) {
//判断该生命周期持有者是否为活跃状态(Started和resume)
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) {
//API不低于16,就用 mChoreographer 来渲染
//在上面的ViewDataBinding构造函数中mFrameCallback最终调用了mRebindRunnable
mChoreographer.postFrameCallback(mFrameCallback);
} else {
//使用UI主线程的 Handler执行Runnable
mUIThreadHandler.post(mRebindRunnable);
}
}
}
不管api多少最后都会执行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.
//view没attach到window是移除监听
mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
return;
}
}
executePendingBindings();
}
};
public void executePendingBindings() {
if (mContainingBinding == null) {
executeBindingsInternal();
} else {
mContainingBinding.executePendingBindings();
}
}
private void executeBindingsInternal() {
...判断数据合规性情形
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;
}
如果存在最后调用的是ActivityMainBindingImpl的executeBindings()
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
...
androidx.databinding.ObservableField<java.lang.String> userName = null;
com.example.myapplication.User user = mUser;
java.lang.String userNameGet = null;
if ((dirtyFlags & 0xfL) != 0) {
if ((dirtyFlags & 0xdL) != 0) {
if (user != null) {
// read user.name
userName = user.getName();
}
//注意这个0是自动生成的localFieldId,以后用得上 =。=
updateRegistration(0, userName);
if (userName != null) {
// read user.name.get()
userNameGet = userName.get();
}
}
...
}
// 根据不同标志位(编译时动态决定),刷新 UI 控件
if ((dirtyFlags & 0xdL) != 0) {
//在这里将数据进行了赋值,根据@+id中定义的id名定义控件
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.idName, userNameGet);
}
}
// dirty flag
private long mDirtyFlags = 0xffffffffffffffffL;
/* flag mapping
flag 0 (0x1L): user.name
flag 1 (0x2L): user
flag 2 (0x3L): null
flag mapping end*/
//end
总结
- 编译过程中会生成带有tag的布局文件(根布局layout/XXX_0,其他binding_数字)
- DataBindingUtil.setContentView根据编译期间新生成的布局文件的tag得到相应布局的ViewBinding的实现类
- 将布局的子View,全部存放在数组中,然后将数组中的View赋值给生成的ViewBinding的属性,这样就可以直接使用生成的ViewBinding的属性来使用view,无需再调用findViewById了。bindings[index] = view。数据的下标就是根据tag的数字生成的
- 根据不同标志位(编译时动态决定),刷新相应的 UI 控件。
mDirtyFlags标记位非常重要,如果使用了ObservableField等用于自动刷新的类,则在标记位就会包含相应的映射,如果使用的是String、Int等数据类型,则不会包含。
动态设置数据绑定有2种方式:
第一种:实体类继承BaseObservable,或者自己实现Observable;使用ObservableField<>,泛型可以填入自己需要的类型,注意必须要初始化。对于基本数据类型也可以直接使用ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble和ObservableParcelable;属性设为public。
第二种:实体类继承BaseObservable,或者自己实现Observable;在需要刷新的属性的get方法上添加@Bindable注解,此时会自动生成BR类(很多时候BR文件不会自动生成,那就重启AS吧...);在相应的set方法里调用notifyPropertyChanged(BR.xxx)进行刷新。
上面是第一种方式的executeBindings,我们看看第二种方式的
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
java.lang.String userName = null;
com.example.myapplication.User user = mUser;
if ((dirtyFlags & 0x3L) != 0) {
if (user != null) {
// read user.name
userName = user.getName();
...
}
}
// batch finished
if ((dirtyFlags & 0x3L) != 0) {
// api target 1
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.idName, userName);
...
}
}
private long mDirtyFlags = 0xffffffffffffffffL;
/* flag mapping
flag 0 (0x1L): user
flag 1 (0x2L): null
flag mapping end*/
mDirtyFlags的映射也发生了改变。对比两种情况,除了标志位的变化之后还有在进行属性赋值的时候,如果是ObservableField类型的数据都会将属性值作为参数去调用updateRegistration()方法
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
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;
}
//属性监听类,还有其他类型CREATE_LIST_LISTENER、CREATE_MAP_LISTENER、
//CREATE_LIVE_DATA_LISTENER
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
}
};
进入registerTo方法,看一下具体实现:
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return;
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
//listenerCreator.create会创建一个WeakListener对象
//通过构造方法创建WeakPropertyListener对象,又通过getListener返回WeakListener对象
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
if (mLifecycleOwner != null) {
listener.setLifecycleOwner(mLifecycleOwner);
}
}
listener.setTarget(observable);
}
进入setTarget()
public void setTarget(T object) {
unregister();
//mTarget就是ObservableField对象
mTarget = object;
if (mTarget != null) {
//mObservable是在使用构造函数的时候进行创建的,所以实际上是一个WeakPropertyListener类型
//mListener = new WeakListener<Observable>(binder, localFieldId, this);
mObservable.addListener(mTarget);
}
}
//添加属性改变监听
public void addListener(Observable target) {
target.addOnPropertyChangedCallback(this);
}
addListener()方法中的target就是ObservableField对象,它是一个被观察者。通过addOnPropertyChangedCallback()方法,相当于向target注册一个观察者
public interface Observable {
void addOnPropertyChangedCallback(OnPropertyChangedCallback callback);
void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback);
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);
}
}
在WeakPropertyListener中的具体实现:
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 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 void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
if (mInLiveDataRegisterObserver) {
// We're in LiveData registration, which always results in a field change
// that we can ignore. The value will be read immediately after anyway, so
// there is no need to be dirty.
return;
}
//判断数据是否发生了更改
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
//与前面过程相同,最终更新UI的数据。
requestRebind();
}
}
onFieldChange用于判断数据是否发生了更改,看一下在ActivityMainBindingImpl中的具体实现:
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
//localFieldId是在executeBindings方法里面自动生成,从updateRegistration一步一步传过来的
case 0 :
return onChangeUserName((androidx.databinding.ObservableField<java.lang.String>) object, fieldId);
...
}
return false;
}
private boolean onChangeUserName(androidx.databinding.ObservableField<java.lang.String> UserName, int fieldId) {
if (fieldId == BR._all) {
synchronized(this) {
//根据前面的标志位映射的注释可知,这个方法用来判断user.name是否发生了变化
//flag 0 (0x1L): user.name
mDirtyFlags |= 0x1L;
}
return true;
}
return false;
}
总结
当数据源发生变化,会回调WeakPropertyListener的onPropertyChanged()方法。在WeakPropertyListener中持有ViewDataBinding对象,调用该对象的handleFieldChange()方法,去更新View。
到这,DataBinding的启动部分分析完了。
反向绑定
双向绑定意思不仅数据绑定UI,同时UI更新时可以刷新数据,语法为@={}。我们看下反向绑定的过程。CheckBox 继承 CompoundButton我们用CompoundButton的checked属性结合CompoundButtonBindingAdapter源码来说明反向绑定的过程。
@BindingMethods({
@BindingMethod(type = CompoundButton.class, attribute = "android:buttonTint", method = "setButtonTintList"),
@BindingMethod(type = CompoundButton.class, attribute = "android:onCheckedChanged", method = "setOnCheckedChangeListener"),
})
@InverseBindingMethods({
@InverseBindingMethod(type = CompoundButton.class, attribute = "android:checked"),
})
public class CompoundButtonBindingAdapter {
@BindingAdapter("android:checked")
public static void setChecked(CompoundButton view, boolean checked) {
if (view.isChecked() != checked) {
view.setChecked(checked);
}
}
@BindingAdapter(value = {"android:onCheckedChanged", "android:checkedAttrChanged"},
requireAll = false)
public static void setListeners(CompoundButton view, final OnCheckedChangeListener listener,
final InverseBindingListener attrChange) {
if (attrChange == null) {
view.setOnCheckedChangeListener(listener);
} else {
view.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (listener != null) {
listener.onCheckedChanged(buttonView, isChecked);
}
attrChange.onChange();
}
});
}
}
}
@InverseBindingMethods:元素为@InverseBindingMethod的一个数组,用来注解 类 。
@InverseBindingMethod:反向绑定方法,用来确定怎么去监听view属性的变化和回调哪一个getter方法。包含以下4个属性:
- type:包含attribute的view类型。
- attribute:支持双向绑定的属性(string格式)。
- event:可以省略,用来通知DataBinding系统attribute已经改变,默认为attribute + "AttrChanged"。(UI通知数据)
- method:可以省略,用来从view获取数据的方法,不设定就会自动寻找"is" 或 "get"+attribute方法。
@InverseBindingMethod(type = CompoundButton.class, attribute = "android:checked")省略了事件event(checkedAttrChanged)和method(isChecked)
@BindingAdapter注解设置事件调用时机
@BindingAdapter("android:checked")
checkbox中使用checked属性时,会自动调用setChecked方法.。
我们在layout中使用双向绑定
<CheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={user.checked}"/>
当Checked属性变化时触发事件checkeAttrChanged,通过bindingAdapter注解调用setListeners,使用了InverseBindingListener接口以供用户将事件通知抛出。InverseBindingListener接口具体实现代码由框架生成,总得来说就是获取控件属性的当前值,然后用此值更新数据。
InverseBindingListener是个抽象接口,有一个onChange方法,看下layout的binding类中自动生成的InverseBindingListener实现。
private android.databinding.InverseBindingListener checkboxandroidCheck = new android.databinding.InverseBindingListener() {
@Override
public void onChange() {
//这段逻辑其实就是用来更新user实体类中的checked字段的
// Inverse of user.checked.get()
// is user.checked.set((java.lang.Boolean) callbackArg_0)
boolean callbackArg_0 = checkbox.isChecked();//其实就是method
// localize variables for thread safety
// user.checked != null
boolean checkedUserObjectnul = false;
// user.checked
android.databinding.ObservableField<java.lang.Boolean> checkedUser = null;
// user
com.example.myapplication.User user = mUser;
// user.checked.get()
java.lang.Boolean CheckedUser1 = null;
// user != null
boolean userObjectnull = false;
userObjectnull = (user) != (null);
if (userObjectnull) {
checkedUser = user.checked;
checkedUserObjectnul = (checkedUser) != (null);
if (checkedUserObjectnul) {
checkedUser.set((java.lang.Boolean) (callbackArg_0));
}
}
}
};
总结
整个反向绑定的流程下来其实就是:
- @InverseBindingMethod(type = CompoundButton.class, attribute = "android:checked",event="checkedAttrChanged",method="isChecked" )
定义需要反向绑定的属性(checked),并配置event(checkedAttrChanged)和method(isChecked)。
@BindingAdapter(value = {"android:onCheckedChanged","android:checkedAttrChanged"},requireAll = false) 系统会自动根据event找到对应的方法(setListeners),配置好调用时机。setListeners中调用InverseBindingListener.onchange()
在layout中使用双向绑定android:checked="@={user.checked}。
自动在binding类中生成一个InverseBindingListener实现。回调方法onChange中刷新数据
查看TextViewBindingAdapter发现还有未讲到的点
@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
public static String getTextString(TextView view) {
return view.getText().toString();
}
这个注解的方法就相当于作用在view上获取数据的getter方法。
- attribute:支持双向绑定的属性(string格式)。
- event:可以省略,用来通知DataBinding系统attribute已经改变的事件,默认为attribute + "AttrChanged"。需要通过@BindingAdapter进行设置调用时机。
思考:
1、事件驱动反向绑定成功后,UI改变数据也会发生变化,按正常逻辑来讲,将继续触发单向绑定,为什么不会陷入无限循环中呢?
为中断这种循环,通常的做法是在更新UI前,校验新旧数据是否相同,如果相同则不进行刷新动作。比如TextView,其内部的setText方法并不会检验新旧数据的一致性问题,所以在TextViewBindingAdapter内重新绑定了android:text属性,添加校验逻辑。
@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
//校验
final CharSequence oldText = view.getText();
if (text == oldText || (text == null && oldText.length() == 0)) {
return;
}
...
view.setText(text);
}
2、DataBinding与ButterKnife的区别
ButterKnife插件采用注解的方式来查找控件和注册监听,减少重复代码,但还是不够简洁:比如给TextView设置文案必须调用setText();要获取editText的内容前必须拿到editText对象;给view设置监听前也必须要获取这个view对象等。但是使用了DataBinding之后,这些冗余统统可以得到简化,从而只需要专注于业务逻辑了。
------------------------------欢迎多多批评指正