关于DataBinding的使用可以直接参考官网数据绑定库介绍,此处就直接忽略。本篇主要是阐述DataBinding是如何实现liveData的数据更新到对应xml布局
准备Demo直接clone如下demo
git clone https://github.com/xiaobaoyihao/AndroIDDataBindingDemo.git
该样例就是实现最简单的DataBinding
绑定类绑定类是用于访问布局的变量和视图,所有绑定类都是继承VIEwDataBinding抽象类的。
demo中引入了数据绑定和视图绑定,所以系统会为每个布局文件生成一个绑定类。默认情况下,类名称基于布局文件的名称,它会转换为 Pascal 大小写形式并在末尾添加 Binding 后缀。以本demo中fragment_main.xml为例生成的对应类为 FragmentMainBinding。关于绑定类的使用可以直接参考官网使用文档
我们以本例中FragmentMainBinding为例进行分析
我们先看MainFragment代码
class MainFragment : Fragment() { companion object { fun newInstance() = MainFragment() } private lateinit var viewmodel: Mainviewmodel private lateinit var mBinding: FragmentMainBinding overrIDe fun onCreateVIEw( inflater: LayoutInflater, container: VIEwGroup?, savedInstanceState: Bundle? ): VIEw { mBinding = FragmentMainBinding.inflate(layoutInflater, container, false) return mBinding.root } overrIDe fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) viewmodel = viewmodelProvIDer(this).get(Mainviewmodel::class.java) mBinding.lifecycleOwner = vIEwlifecycleOwner mBinding.fvm = viewmodel }}
MainFragment执行了执行了绑定类的inflate静态方法返回了绑定类对象,而其中root成员变量就是对应布局文件的根视图
对应布局
<?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> <variable name="fvm" type="com.dbs.databinding.demo.ui.main.Mainviewmodel" /> </data> <androIDx.constraintlayout.Widget.ConstraintLayout androID:ID="@+ID/main" androID:layout_wIDth="match_parent" androID:layout_height="match_parent" tools:context="com.dbs.databinding.demo.ui.main.MainFragment"> <TextVIEw androID:ID="@+ID/message" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:gravity="center" androID:text="@{String.valueOf(fvm.MClickedCount)}" androID:textSize="25dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constrainttop_totopOf="parent" tools:text="0" /> <button androID:ID="@+ID/btn" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:layout_margintop="20dp" androID:onClick="@{fvm::onClickBtn}" androID:text="button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constrainttop_toBottomOf="@ID/message" /> </androIDx.constraintlayout.Widget.ConstraintLayout></layout>
viewmodel也非常简单
class Mainviewmodel : viewmodel() { var mClickedCount = mutablelivedata(0) fun onClickBtn(vIEw: VIEw) { // mClickCount++ mClickedCount.value = mClickedCount.value?.inc()?: 0 }}
主要实现就是通过按钮点击来实现TextVIEw文本修改;为了有针对性分析,我们以问题形式进行展开分析说明
Q1:FragmentMainBinding.inflate返回了什么?首先我们从build/data_binding_base_class_source_out目录下找到这个FragmentMainBinding
// FragmentMainBinding.java@NonNullpublic static FragmentMainBinding inflate(@NonNull LayoutInflater inflater, @Nullable VIEwGroup root, boolean attachToRoot) { return inflate(inflater, root, attachToRoot, DataBindingUtil.getDefaultComponent());}@NonNull@Deprecatedpublic static FragmentMainBinding inflate(@NonNull LayoutInflater inflater, @Nullable VIEwGroup root, boolean attachToRoot, @Nullable Object component) { return VIEwDataBinding.<FragmentMainBinding>inflateInternal(inflater, R.layout.fragment_main, root, attachToRoot, component);}
// VIEwDataBinding.javaprotected static <T extends VIEwDataBinding> T inflateInternal( @NonNull LayoutInflater inflater, int layoutID, @Nullable VIEwGroup parent, boolean attachtoparent, @Nullable Object bindingComponent) { return DataBindingUtil.inflate( inflater, layoutID, parent, attachtoparent, checkAndCastToBindingComponent(bindingComponent) ); }
public class DataBindingUtil { private static DataBinderMapper sMapper = new DataBinderMapperImpl(); public static <T extends VIEwDataBinding> T inflate( @NonNull LayoutInflater inflater, int layoutID, @Nullable VIEwGroup parent, boolean attachtoparent, @Nullable DataBindingComponent bindingComponent) { final boolean useChildren = parent != null && attachtoparent; final int startChildren = useChildren ? parent.getChildCount() : 0; final VIEw vIEw = inflater.inflate(layoutID, parent, attachtoparent); if (useChildren) { return bindToAddedVIEws(bindingComponent, parent, startChildren, layoutID); } else { return bind(bindingComponent, vIEw, layoutID); } } static <T extends VIEwDataBinding> T bind(DataBindingComponent bindingComponent, VIEw root, int layoutID) { return (T) sMapper.getDataBinder(bindingComponent, root, layoutID); } }
这里面的sMapper类型为androIDx.databinding.DataBinderMapperImpl,需要提下每个模块都有同名的DataBinderMapperImpl,app模块存在二个同名但路径不一样;sMapper可以理解为映射表入口类它引用了appl模块中DataBinderMapperImp
@OverrIDe public VIEwDataBinding getDataBinder(DataBindingComponent bindingComponent, VIEw vIEw, int layoutID) { for(DataBinderMapper mapper : mMappers) { VIEwDataBinding result = mapper.getDataBinder(bindingComponent, vIEw, layoutID); if (result != null) { return result; } } if (loadFeatures()) { return getDataBinder(bindingComponent, vIEw, layoutID); } return null; }
可以看到绑定类的查找是依次遍历项目中所有DataBinderMapperImpl类来搜查绑定类;那么项目中的所有模块的DataBinderMapperImpl是如何添加到mapper列表中呢?我们从上图中看到构造器中调用了addMapper方法
public voID addMapper(DataBinderMapper mapper) { Class<? extends DataBinderMapper> mapperClass = mapper.getClass(); if (mExistingMappers.add(mapperClass)) { mMappers.add(mapper); final List<DataBinderMapper> dependencIEs = mapper.collectDependencIEs(); for(DataBinderMapper dependency : dependencIEs) { addMapper(dependency); } } }
上面代码可以看到mMappers维护了apk所有依赖的DataBinderMapper映射表;先是添加app模块的映射表,然后将其依赖的映射表依次添加到mMappers中;本demo中最终调用了com.example.demo.DataBinderMapperImpl.getDataBinder方法
public class DataBinderMapperImpl extends DataBinderMapper { private static final int LAYOUT_FRAGMENTMAIN = 1; static { INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.demo.R.layout.fragment_main, LAYOUT_FRAGMENTMAIN); } public VIEwDataBinding getDataBinder(DataBindingComponent component, VIEw vIEw, int layoutID) { // 通过布局资源ID以及根vIEw的tag来进行指定绑定类 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_FRAGMENTMAIN: { if ("layout/fragment_main_0".equals(tag)) { return new FragmentMainBindingImpl(component, vIEw); } throw new IllegalArgumentException("The tag for fragment_main is invalID. Received: " + tag); } } } return null; } }
我们反编译下apk
总结下FragmentMainBinding.inflate调用轨迹总结如下
至此我们明白了FragmentMainBinding.inflate返回的其实是FragmentMainBindingImpl对象而非FragmentMainBinding(抽象类)
Q2:绑定类是如何解析并持有vIEw?我们先来看下FragmentMainBinding结构
public FragmentMainBindingImpl(@Nullable androIDx.databinding.DataBindingComponent bindingComponent, @NonNull VIEw root) { this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sVIEwsWithIDs)); } private FragmentMainBindingImpl(androIDx.databinding.DataBindingComponent bindingComponent, VIEw root, Object[] bindings) { super(bindingComponent, root, 1 , (androID.Widget.button) bindings[2] , (androIDx.constraintlayout.Widget.ConstraintLayout) bindings[0] , (androID.Widget.TextVIEw) bindings[1] ); this.btn.setTag(null); this.main.setTag(null); this.message.setTag(null); setRoottag(root); // Listeners invalIDateall(); }
可以看到内部是调用mapBindings方法返回vIEw的数组,然后将该数组的vIEw赋值到绑定类相应的成员变量当中
我们继续看下mapBindings方法
// VIEwDataBinding.javaprotected static Object[] mapBindings(DataBindingComponent bindingComponent, VIEw root, int numBindings, IncludedLayouts includes, SparseIntArray vIEwsWithIDs) { Object[] bindings = new Object[numBindings]; mapBindings(bindingComponent, root, bindings, includes, vIEwsWithIDs, true); return bindings;}private static voID mapBindings(DataBindingComponent bindingComponent, VIEw vIEw, Object[] bindings, IncludedLayouts includes, SparseIntArray vIEwsWithIDs, boolean isRoot) { 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; // 如果是根vIEw,则判断根vIEw的tag是否以layout开头 if (isRoot && tag != null && tag.startsWith("layout")) { final int underscoreIndex = tag.lastIndexOf('_'); //解析根vIEw的tag,将vIEw放到数组的指定位置(位置由xml中的tag后缀数字确定) if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) { final int index = parseTagInt(tag, underscoreIndex + 1); if (bindings[index] == null) { bindings[index] = vIEw; } // 确定绑定布局文件存在include元素布局所在位置 indexInIncludes = includes == null ? -1 : index; isBound = true; } else { indexInIncludes = -1; } } else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) { // 如果tag以binding_开头,说明需要绑定类持有,所以赋值 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; // 如果xml中vIEw声明了ID,绑定类也是需要持有该vIEw 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(); // include布局根vIEw的tag格式:layout/${layout_name}_0 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); } } } }
我们看下apk中fragment_main.xml中vIEw的tag是否是我们预期那样?
至此我们明白了绑定类是如何解析并持有vIEw,它是通过解析布局中vIEw的tag携带的信息存放到数组指定位置中,绑定类构造器就是通过正确读取该数组vIEw,赋值到绑定类的成员变量中,赋值成功后对于子vIEw擦除tag,对于根vIEw则将绑定类绑定到tag中,后面是对vIEw的重绘了Q3:VM中点击事件是如何绑定到视图当中?
因为绑定类构造器中有视图重绘,最终调用invalIDateall,其调用链路如下
我们看下executeBindings部分代码
可以看到执行绑定是会对按钮进行事件绑定,这个事件监听器是OnClickListenerImpl
public static class OnClickListenerImpl implements androID.vIEw.VIEw.OnClickListener{ private com.dbs.databinding.demo.ui.main.Mainviewmodel value; public OnClickListenerImpl setValue(com.dbs.databinding.demo.ui.main.Mainviewmodel value) { this.value = value; return value == null ? null : this; } @OverrIDe public voID onClick(androID.vIEw.VIEw arg0) { this.value.onClickBtn(arg0); } }
因为布局中引用了vm.onClickBtn方法,所以这个事件监听器顺其自然地持有vm;因此绑定类的点击事件绑定最终通过一个包装类来实现事件的传导来实现的
Q4:liveData数据变化是如何更新到vIEw上?
要弄清楚这个问题,我们需要先看看mutablelivedata.setValue
// liveData.java@MainThreadprotected voID setValue(T value) { assertMainThread("setValue"); mVersion++; mData = value; dispatchingValue(null); } voID dispatchingValue(@Nullable ObserverWrapper initiator) { if (mdispatchingValue) { mdispatchInvalIDated = true; return; } mdispatchingValue = true; do { mdispatchInvalIDated = false; if (initiator != null) { consIDerNotify(initiator); initiator = null; } else { for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { consIDerNotify(iterator.next().getValue()); if (mdispatchInvalIDated) { break; } } } } while (mdispatchInvalIDated); mdispatchingValue = false;}
因为dispatchingValue传入了null所以最终执行consIDerNotify通知所有观察者,我们来看下consIDerNotify
private voID consIDerNotify(ObserverWrapper observer) { // 如果liveData处于非活动状态,不进行通知,当视图至少是onStarted时才会通知视图 if (!observer.mActive) { return; } // Check latest state b4 dispatch. Maybe it changed state but we dIDn't get the event yet. // // we still first check observer.active to keep it as the entrance for events. So even if // the observer moved to an active state, if we've not received that event, we better not // notify for a more predictable notification order. if (!observer.shouldBeActive()) { observer.activeStateChanged(false); return; } if (observer.mLastVersion >= mVersion) { return; } observer.mLastVersion = mVersion; observer.mObserver.onChanged((T) mData);}
可以看到最终livedata将变化的数据通知给了所有的观察者的onChanged方法这个方法相信大家都很熟悉了;
这里的观察者observer.mObserver类型其实为lifecycleBoundobserver,通常我们都是调用liveData.observe方法来进行监听data变化
@MainThreadpublic voID observe(@NonNull lifecycleOwner owner, @NonNull Observer<? super T> observer) { assertMainThread("observe"); if (owner.getlifecycle().getCurrentState() == DESTROYED) { // ignore return; } lifecycleBoundobserver wrapper = new lifecycleBoundobserver(owner, observer); ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); if (existing != null && !existing.isAttachedTo(owner)) { throw new IllegalArgumentException("Cannot add the same observer" + " with different lifecycles"); } if (existing != null) { return; } owner.getlifecycle().addobserver(wrapper); }
可以清楚看到observe方法将我们传入的观察者包装成lifecycleBoundobserver对象,它订阅了lifecycle所以有了感知生命周期的能力
总结下基本调用轨迹
到这里我们基本明白了liveData的数据更新是如何通知到所有观察者了。那么绑定类内部是在什么时机订阅liveData呢?我们注意到demo中有这样一行代码
class MainFragment : Fragment() { ... overrIDe fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) viewmodel = viewmodelProvIDer(this).get(Mainviewmodel::class.java) //重点代码 mBinding.lifecycleOwner = vIEwlifecycleOwner mBinding.fvm = viewmodel }}
注意点:对于fragment中建议使用vIEwlifecycleOwner赋值给对应绑定类,而对于activity可以直接使用this,因为activity中mlifecycleRegistry其实是通过弱引用持有activity,所以没有内存泄漏风险
/** * Sets the {@link lifecycleOwner} that should be used for observing changes of * liveData in this binding. If a {@link liveData} is in one of the binding Expressions * and no lifecycleOwner is set, the liveData will not be observed and updates to it * will not be propagated to the UI. * * @param lifecycleOwner The lifecycleOwner that should be used for observing changes of * liveData in this binding. */ @MainThread public voID setlifecycleOwner(@Nullable lifecycleOwner lifecycleOwner) { if (mlifecycleOwner == lifecycleOwner) { return; } if (mlifecycleOwner != null) { mlifecycleOwner.getlifecycle().removeObserver(mOnStartListener); } mlifecycleOwner = lifecycleOwner; if (lifecycleOwner != null) { if (mOnStartListener == null) { mOnStartListener = new OnStartListener(this); } lifecycleOwner.getlifecycle().addobserver(mOnStartListener); } for (WeakListener<?> weakListener : mLocalFIEldobservers) { if (weakListener != null) { weakListener.setlifecycleOwner(lifecycleOwner); } } }
方法注释上写的比较清楚:因为demo中存在binding表达式,如果不调用该方法会导致livedata数据无法更新到vIEw上
其实从上面代码可以看到,首次会创建了OnStartListener对象并注册到lifecycle中,这样单lifecycle进入ON_START生命周期时会自动进行数据绑定,相关实现见下图
紧接找为绑定类中所有的表达式变量的观察者设置lifecycleOwner,这里面的观察者其实就是liveDataListener.mListener;从哪里可以看出,这里面有点复杂,
// FragmentMainBindingImpl.javaprotected voID executeBindings() { ... if ((dirtyFlags & 0x7L) != 0) { if (fvm != null) { // read fvm.MClickedCount fvmMClickedCount = fvm.getMClickedCount(); } updateliveDataRegistration(0, fvmMClickedCount); ... } } protected boolean updateliveDataRegistration(int localFIEldID, liveData<?> observable) { mInliveDataRegisterObserver = true; try { return updateRegistration(localFIEldID, observable, CREATE_liVE_DATA_ListENER); } finally { mInliveDataRegisterObserver = false; } }private boolean updateRegistration(int localFIEldID, Object observable, CreateWeakListener ListenerCreator) { // 如果livedata为空,卸载之前的旧监听器 if (observable == null) { return unregisterFrom(localFIEldID); } WeakListener Listener = mLocalFIEldobservers[localFIEldID]; if (Listener == null) { // 如果对应的表达式变量监听器为空,创建一个,并注册 registerTo(localFIEldID, observable, ListenerCreator); return true; } // 如果绑定的livedata是同一个,不需要做处理 if (Listener.getTarget() == observable) { return false;//nothing to do, same object } // 如果不是同一个,则需要先卸载,并重新注册 unregisterFrom(localFIEldID); registerTo(localFIEldID, observable, ListenerCreator); return true; }/** * @hIDe */protected voID registerTo(int localFIEldID, Object observable, CreateWeakListener ListenerCreator) { if (observable == null) { return; } WeakListener Listener = mLocalFIEldobservers[localFIEldID]; if (Listener == null) { Listener = ListenerCreator.create(this, localFIEldID); mLocalFIEldobservers[localFIEldID] = Listener; if (mlifecycleOwner != null) { Listener.setlifecycleOwner(mlifecycleOwner); } } Listener.setTarget(observable);}
最终调用了liveDataListener.mListener的setlifecycleOwner、setTarget方法,最终又会调用liveDataListener的setlifecycleOwner、addListener方法,贴下liveDataListener
private static class liveDataListener implements Observer, ObservableReference<liveData<?>> { final WeakListener<liveData<?>> mListener; lifecycleOwner mlifecycleOwner; public liveDataListener(VIEwDataBinding binder, int localFIEldID) { mListener = new WeakListener(binder, localFIEldID, this); } @OverrIDe public voID setlifecycleOwner(lifecycleOwner lifecycleOwner) { // 主要是卸载,重新注册二个功能 lifecycleOwner owner = (lifecycleOwner) lifecycleOwner; liveData<?> liveData = mListener.getTarget(); if (liveData != null) { if (mlifecycleOwner != null) { liveData.removeObserver(this); } if (lifecycleOwner != null) { liveData.observe(owner, this); } } mlifecycleOwner = owner; } @OverrIDe public WeakListener<liveData<?>> getListener() { return mListener; } @OverrIDe public voID addListener(liveData<?> target) { if (mlifecycleOwner != null) { // 监听liveData数据变化 target.observe(mlifecycleOwner, this); } } @OverrIDe public voID removeListener(liveData<?> target) { target.removeObserver(this); } @OverrIDe public voID onChanged(@Nullable Object o) { VIEwDataBinding binder = mListener.getBinder(); if (binder != null) { // 注意此处最后参数为0,这样后面的onFIEldChange返回值必定为true,所以必定会执行requestRebind方法 binder.handleFIEldChange(mListener.mLocalFIEldID, mListener.getTarget(), 0); } } }
上述的二个 *** 作完成后,每当livedata变化时都会通知到liveDataListener.onChanged方法,改方法中会对mDirtyFlags做相关修改,并进行重新调用绑定方法,以实现绑定表达式的更新
// VIEwDataBinding.javaprivate 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; } // 修改mDirtyFlags位 boolean result = onFIEldChange(mLocalFIEldID, object, fIEldID); if (result) { requestRebind(); } }
requestRebind后会最终又会调用executeBindings这样就实现了livedata数据变化及时更新到vIEw中;Q4问题比较绕,需要仔细揣摩下,至此AndroID-DataBindging基本的几个问题搞清楚了,我们对AndorID-DataBinding有更深入理解,后面的其他延伸的东西都可以触旁类推了
总结以上是内存溢出为你收集整理的Android-DataBinding源码探究全部内容,希望文章能够帮你解决Android-DataBinding源码探究所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)