JetPack之DataBinding布局文件绑定及UI刷新原理

1. 项目结构说明

项目有三个moduel:app、business,其中app为空moduel,里面没有具体代码,business主要放业务相关的代码,又有两个目录home和baseui。在app moduel和baseui包和home包的build目录下生成的代码目录如下:
在这里插入图片描述

可以看到在四个目录中分别都生成了一个DataBinderMapperImpl类。这里说明两点:

  1. 对于每个基本的布局文件,开启DataBinding后会在编译时生成以“Binding”结尾的java文件;
  2. 对于含有<layout>标签的布局文件,开启DataBinding后不仅会在编译时生成以“Binding”结尾的java文件,还会生成以“BindingImpl”结尾的java文件。

2. DataBinding绑定解析——节点依赖关系

以DataBindingUtil的inflate()方法为切入点来分析绑定过程。

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

在Fragment的onCreateView()方法中初始化布局时往往给attachToParent参数传值为false,因此会执行bind()方法,bindingComponent默认情况下为null。

//DataBindingUtil.kt
private static DataBinderMapper sMapper = new DataBinderMapperImpl();

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

这里要注意sMapper到底是上面生成的三个DataBinderMapperImpl中的哪一个,DataBindingUtil的包名为androidx.databinding,注意到app moduel下的第一个DataBinderMapperImpl的包名也是androidx.databinding,因此就是这个类了:

package androidx.databinding;

public class DataBinderMapperImpl extends MergedDataBinderMapper {
  DataBinderMapperImpl() {
    addMapper(new com.yy.oms.logistics.DataBinderMapperImpl());
  }
}

该类的代码十分简单,起作用是做为查找其他节点的入口,我将其称做“根DataBinderMapperImpl”,在构造函数中调用了父类MergedDataBinderMapper的addMapper()方法:

//MergedDataBinderMapper.java

// we keep set of existing classes so that addMapper can avoid re-adding same class.
// usually not necessary as list lookup might be sufficient but if the project has 100+
// modules, it might matter, hence we have a fast lookup as well.
private Set<Class<? extends DataBinderMapper>> mExistingMappers = new HashSet<>();
private List<DataBinderMapper> mMappers = new CopyOnWriteArrayList<>();

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

这里递归调用addMapper()方法将mapper及其依赖项添加到mExistingMappers及mMappers中。从上面代码可知这里的形参mapper是com.yy.oms.logistics包下的DataBinderMapperImpl类,因此将其添加到map中后,会递归的将其依赖的app下的第二个DataBinderMapperImpl添加到map中。

//com.yy.oms.logistics.DataBinderMapperImpl.java
public List<DataBinderMapper> collectDependencies() {
	  ArrayList<DataBinderMapper> result = new ArrayList<DataBinderMapper>(3);
	  result.add(new androidx.databinding.library.baseAdapters.DataBinderMapperImpl());
	  result.add(new com.yy.oms.baseui.DataBinderMapperImpl());
	  result.add(new com.yy.oms.home.DataBinderMapperImpl());
	  return result;
}

可以看到根DataBinderMapperImpl的依赖项有三个,第一个是系统提供的DataBinderMapperImpl,其余两个就是上图中项目编译生成的两外两个DataBinderMapperImpl。总结上述各个节点间的依赖关系为:
在这里插入图片描述

3. DataBinding绑定解析——获取绑定

将所有节点添加完毕后回到DataBindingUtil的bind()方法中,接下来会调用DataBinderMapperImpl类的getDataBinder()方法,这里会调用到生成的第一个DataBinderMapperImpl的父类MergedDataBinderMapper中的方法:

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

这里会遍历mMappers集合,再分别调用各个元素的getDataBinder()方法,假设我们要获取home目录下的资源文件的绑定文件,最终会在com.yy.oms.home.DataBinderMapperImpl的getDataBinder()方法中查到结果:

public class DataBinderMapperImpl extends DataBinderMapper {
  private static final int LAYOUT_ITEMORDERLIST = 1;

  private static final int LAYOUT_ITEMORDERLIST2 = 2;

  private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(2);

  static {
    INTERNAL_LAYOUT_ID_LOOKUP.put(com.yy.oms.home.R.layout.item_order_list, LAYOUT_ITEMORDERLIST);
    INTERNAL_LAYOUT_ID_LOOKUP.put(com.yy.oms.home.R.layout.item_order_list2, LAYOUT_ITEMORDERLIST2);
  }

  @Override
  public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
    int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
    if(localizedLayoutId > 0) {
      final Object tag = view.getTag();
      if(tag == null) {
        throw new RuntimeException("view must have a tag");
      }
      switch(localizedLayoutId) {
        case  LAYOUT_ITEMORDERLIST: {
          if ("layout/item_order_list_0".equals(tag)) {
            return new ItemOrderListBindingImpl(component, view);
          }
          throw new IllegalArgumentException("The tag for item_order_list is invalid. Received: " + tag);
        }
        case  LAYOUT_ITEMORDERLIST2: {
          if ("layout/item_order_list2_0".equals(tag)) {
            return new ItemOrderList2BindingImpl(component, view);
          }
          throw new IllegalArgumentException("The tag for item_order_list2 is invalid. Received: " + tag);
        }
      }
    }
    return null;
  }
}

这里会通过tag来获取判断到底返回哪一个绑定,是因为编译器会对xml做处理,看一下item_order_list2.xml处理过后的文件,文件路径为:/Users/zengwenbing/Desktop/WorkSpace/work-xxxxx/xxxxx-android/business/home/build/intermediates/incremental/packageDebugResources/stripped.dir/layout

<?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:background="@drawable/shape_order_list_item"
        android:tag="layout/item_order_list2_0"           
        android:layout_width="170dp"
        android:layout_height="170dp"  xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto">

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            app:layout_constraintTop_toTopOf="parent" />

        <ImageView
            android:id="@+id/image"
            android:layout_width="26dp"
            android:layout_height="26dp"
            android:src="@drawable/shop"
            android:layout_marginStart="10dp"
            android:layout_marginTop="10dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent" />

        <TextView
            android:id="@+id/seller_name"
            android:tag="binding_1"                        
            android:textColor="#000000"
            android:textSize="13sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="2dp"
            android:layout_marginStart="5dp"
            android:textIsSelectable="true"
            android:fontFamily="sans-serif-light"
            app:layout_constraintStart_toEndOf="@id/image"
            app:layout_constraintTop_toTopOf="@id/image"
            app:layout_constraintBottom_toBottomOf="@id/image"
            tools:text="seller name"/>

        <TextView
            android:id="@+id/order_status"
            android:tag="binding_2"                 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#FF9232"
            android:padding="2dp"
            android:layout_marginEnd="8dp"
            app:layout_constraintTop_toTopOf="@id/seller_name"
            app:layout_constraintBottom_toBottomOf="@id/seller_name"
            app:layout_constraintEnd_toEndOf="parent"
            tools:text="on delivery"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

再对比一下原文件:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <import type="com.yy.oms.logistics.home.uitls.DateUtil" />
        <variable
            name="mOrderModel"
            type="com.yy.oms.logistics.home.model.OrderListItemModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:background="@drawable/shape_order_list_item"
        android:onClick="@{mOrderModel.toLogisticsDetail}"
        android:layout_width="170dp"
        android:layout_height="170dp">

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            app:layout_constraintTop_toTopOf="parent" />

        <ImageView
            android:id="@+id/image"
            android:layout_width="26dp"
            android:layout_height="26dp"
            android:src="@drawable/shop"
            android:layout_marginStart="10dp"
            android:layout_marginTop="10dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent" />

        <TextView
            android:id="@+id/seller_name"
            android:text="@{mOrderModel.order.seller_name}"
            android:textColor="#000000"
            android:textSize="13sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="2dp"
            android:layout_marginStart="5dp"
            android:textIsSelectable="true"
            android:fontFamily="sans-serif-light"
            app:layout_constraintStart_toEndOf="@id/image"
            app:layout_constraintTop_toTopOf="@id/image"
            app:layout_constraintBottom_toBottomOf="@id/image"
            tools:text="seller name"/>

        <TextView
            android:id="@+id/order_status"
            android:text="@{mOrderModel.statusText}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#FF9232"
            android:padding="2dp"
            android:layout_marginEnd="8dp"
            app:layout_constraintTop_toTopOf="@id/seller_name"
            app:layout_constraintBottom_toBottomOf="@id/seller_name"
            app:layout_constraintEnd_toEndOf="parent"
            tools:text="on delivery"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

通过对比可以有两点结论:

  1. 对于处理后的布局xml,里边把除了普通布局外的东西都删除(就是把data,layout去除,只留下布局,也会把表达式也去除)
  2. 同时在每个view上设置了tag,根view命名是“layout/xml文件名_0”,子view命名是“binding_1++”

因此可以通过根View的tag来判断到底初始化哪一个绑定,这里会初始化ItemOrderList2BindingImpl实例并返回。下面会执行ItemOrderList2BindingImpl的构造函数:

public ItemOrderList2BindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
        this(bindingComponent, root, mapBindings(bindingComponent, root, 10, sIncludes, sViewsWithIds));
    }
    private ItemOrderList2BindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
        super(bindingComponent, root, 0
            , (android.widget.TextView) bindings[4]
            , (android.widget.ImageView) bindings[9]
            , (android.widget.TextView) bindings[7]
            , (android.widget.TextView) bindings[3]
            , (android.widget.TextView) bindings[2]
            , (android.widget.TextView) bindings[6]
            , (android.widget.TextView) bindings[5]
            , (android.widget.TextView) bindings[1]
            , (android.widget.TextView) bindings[8]
            );
        this.expressNo.setTag(null);
        this.logisticsDetail.setTag(null);
        this.logisticsNo.setTag(null);
        this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];
        this.mboundView0.setTag(null);
        this.orderStatus.setTag(null);
        this.orderTime.setTag(null);
        this.paymentAmount.setTag(null);
        this.sellerName.setTag(null);
        this.updateState.setTag(null);
        setRootTag(root);
        // listeners
        invalidateAll();
    }

bindingComponent默认为null,root为根View,mapBindings()函数的作用是将根View通过tag解析并保存在Object[numBindings]中,其下标即是tag中“_”后的数字。看一下解析的过程,代码有点长:

private static void mapBindings(DataBindingComponent bindingComponent, View view,
        Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
        boolean isRoot) {
    final int indexInIncludes;
    //解析过的View会给其设置tag为R.id.dataBinding(setRootTag()方法),
    //这里通过该tag来判断是否已经映射过
    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的tag以layout开头
    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;
        }
    //子View的tag以binding开头,BINDING_TAG_PREFIX = binding_
    } 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 view
        indexInIncludes = -1;
    }

	//没有通过xml绑定的,通过传入的viewsWithIds参数进行映射
    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;
            }
        }
    }

	//对于ViewGroup还要对齐子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;
            //映射<include>,其通过类型为IncludedLayouts的includes参数传入
            if (indexInIncludes >= 0 && child.getTag() instanceof String) {
                String childTag = (String) child.getTag();
                //<include>文件中的根View
                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;
                        }
                    }
                }
            }
            //如果不是<include>,则就是ViewGroup中的子View,就递归调用映射该子View
            if (!isInclude) {
                mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
            }
        }
    }
}

映射完View,回到ItemOrderList2BindingImpl的构造方法,先调用super()方法,即ItemOrderList2Binding的构造方法:

protected ItemOrderList2Binding(Object _bindingComponent, View _root, int _localFieldCount,
      TextView expressNo, ImageView image, TextView logisticsDetail, TextView logisticsNo,
      TextView orderStatus, TextView orderTime, TextView paymentAmount, TextView sellerName,
      TextView updateState) {
    super(_bindingComponent, _root, _localFieldCount);
    this.expressNo = expressNo;
    this.image = image;
    this.logisticsDetail = logisticsDetail;
    this.logisticsNo = logisticsNo;
    this.orderStatus = orderStatus;
    this.orderTime = orderTime;
    this.paymentAmount = paymentAmount;
    this.sellerName = sellerName;
    this.updateState = updateState;
  }

这里除了将映射的View设置给ItemOrderList2Binding的成员函数外,还会调用其父类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");
    }
    //设置mFrameCallback和mUIThreadHandler用于在主线程执行绑定UI
    if (USE_CHOREOGRAPHER) {
        mChoreographer = Choreographer.getInstance();
        mFrameCallback = new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                mRebindRunnable.run();
            }
        };
    } else {
        mFrameCallback = null;
        mUIThreadHandler = new Handler(Looper.myLooper());
    }
}

这里主要初始化了mLocalFieldObservers数组,还设置了触发绑定UI时的mRebindRunnable,看一下mRebindRunnable的定义:

private final Runnable mRebindRunnable = new Runnable() {
    @Override
    public void run() {
        synchronized (this) {
            mPendingRebind = false;
        }
        processReferenceQueue();

        if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
            // Nested so that we don't get a lint warning in IntelliJ
            if (!mRoot.isAttachedToWindow()) {
                // Don't execute the pending bindings until the View
                // is attached again.
                mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
                mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
                return;
            }
        }
        executePendingBindings();
    }
};

主要调用executePendingBindings()方法执行绑定UI。

4. DataBinding绑定解析——执行绑定

获取到绑定后,若要立即执行UI绑定会执行executeBindings():

//class ItemOrderList2BindingImpl
protected void executeBindings() {

        java.lang.String mOrderModelOrderSellerName = null;
        androidx.databinding.ObservableField<java.lang.Integer> mOrderModelTempObservable = null;

        if ((dirtyFlags & 0x7L) != 0) {
            if ((dirtyFlags & 0x6L) != 0) {
                    if (mOrderModelOrder != null) {
                        // read mOrderModel.order.seller_name
                        mOrderModelOrderSellerName = mOrderModelOrder.getSeller_name();
                    }
            }

                if (mOrderModel != null) {
                    // read mOrderModel.tempObservable
                    mOrderModelTempObservable = mOrderModel.getTempObservable();
                }
                updateRegistration(0, mOrderModelTempObservable);

                // read androidx.databinding.ViewDataBinding.safeUnbox(mOrderModel.tempObservable.get())
                androidxDatabindingViewDataBindingSafeUnboxMOrderModelTempObservableGet = androidx.databinding.ViewDataBinding.safeUnbox(mOrderModelTempObservableGet);
        }
        // batch finished
        if ((dirtyFlags & 0x6L) != 0) {
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.sellerName, mOrderModelOrderSellerName);
        }
        if ((dirtyFlags & 0x7L) != 0) {
            // api target 1
 this.logisticsNo.setText(androidxDatabindingViewDataBindingSafeUnboxMOrderModelTempObservableGet);
        }
    }

删除了冗余代码只留下一个String类型和一个ObservableFile类型的变量。对于String类型的mOrderModelOrderSellerName,每次执行绑定即先读mOrderModel.order.seller_name的值,然后设置给TextVIew sellerName,设置的过程是通过BindingAdapter实现的:

//androidx.databinding.adapters.TextViewBindingAdapter
@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;
    }
    if (text instanceof Spanned) {
        if (text.equals(oldText)) {
            return; // No change in the spans, so don't set anything.
        }
    } else if (!haveContentsChanged(text, oldText)) {
        return; // No content changes, so don't set anything.
    }
    view.setText(text);
}

对于ObservsbleFile类型的变量不仅直接调用TextView的setText()方法设置文字,还需要更新该ObservsbleFile对象注册的监听器:

//ViewDataBinding.java
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
    @Override
    public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
        return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
    }
};

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

private boolean updateRegistration(int localFieldId, Object observable,
        CreateWeakListener listenerCreator) {
    //若该localFieldId对应一个null,无意义需要反注册
    if (observable == null) {
        return unregisterFrom(localFieldId);
    }
    WeakListener listener = mLocalFieldObservers[localFieldId];
    //若本地Field-Observable缓存中以localFieldId为下标不存在WeakListener对象,则需要重新注册
    if (listener == null) {
        registerTo(localFieldId, observable, listenerCreator);
        return true;
    }
    //若本地Field-Observable缓存中以localFieldId为下标存在WeakListener对象且该对象的target与observable相同,
    //则什么都不做
    if (listener.getTarget() == observable) {
        return false;//nothing to do, same object
    }
    //若本地Field-Observable缓存中以localFieldId为下标存在WeakListener对象且该对象的target与observable不相同,
    //则需要反这册后再重新注册
    unregisterFrom(localFieldId);
    registerTo(localFieldId, observable, listenerCreator);
    return true;
}

以上注释中说明了更新的逻辑,下面来看一反注册与注册的逻辑:

//ViewDataBinding.java
protected boolean unregisterFrom(int localFieldId) {
    WeakListener listener = mLocalFieldObservers[localFieldId];
    if (listener != null) {
        return listener.unregister();
    }
    return false;
}

//class WeakListener
public boolean unregister() {
    boolean unregistered = false;
    if (mTarget != null) {
        mObservable.removeListener(mTarget);
        unregistered = true;
    }
    mTarget = null;
    return unregistered;
}
/************************************************************************************************/
//ViewDataBinding.java
protected void registerTo(int localFieldId, Object observable,
        CreateWeakListener listenerCreator) {
    if (observable == null) {
        return;
    }
    WeakListener listener = mLocalFieldObservers[localFieldId];
    if (listener == null) {
    	//CREATE_PROPERTY_LISTENER的create()函数会创建一个WeakPropertyListener
        listener = listenerCreator.create(this, localFieldId);
        mLocalFieldObservers[localFieldId] = listener;
        if (mLifecycleOwner != null) {
            listener.setLifecycleOwner(mLifecycleOwner);
        }
    }
    listener.setTarget(observable);
}

//class WeakListener
public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
    mObservable.setLifecycleOwner(lifecycleOwner);
}

public void setTarget(T object) {
    unregister();
    mTarget = object;
    if (mTarget != null) {
        mObservable.addListener(mTarget);
    }
}

反注册的逻辑是:

  1. 将localFieldId对应的WeakListener对象找到;
  2. 然后将WeakListener对象的mObservable成员对象的mTarget监听移除;
  3. 再将mTarget置为null。

注册的逻辑是:
4. 创建一个类型为WeakPropertyListener的listener;
5. 将该listener添加到mLocalFieldObservers缓存中;
6. 若mLifecycleOwner不为空,则给该listener设置生命周期拥有者;
7. 给该listener设置target。

5. DataBinding绑定解析——刷新UI

上一节给WeakPropertyListener设置了target,注意到这里的WeakPropertyListener,UI刷新就是依靠该类型的监听器,除此之外是还有其他类型的监听器,来看看他们的构造函数:

public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
    mListener = new WeakListener<Observable>(binder, localFieldId, this);
}

public WeakListListener(ViewDataBinding binder, int localFieldId) {
    mListener = new WeakListener<ObservableList>(binder, localFieldId, this);
}

public WeakMapListener(ViewDataBinding binder, int localFieldId) {
    mListener = new WeakListener<ObservableMap>(binder, localFieldId, this);
}

public LiveDataListener(ViewDataBinding binder, int localFieldId) {
    mListener = new WeakListener<LiveData>(binder, localFieldId, this);
}

这里的泛型即是WeakListener类中成员变量target的类型,可知这里可以绑定四种类型的targe:Observable、ObservableList、ObservableMap和LiveData。回到上面setTarget()函数,关注mObservable的类型是接口类型ObservableReference,这里我们看两种它的实现:

//class WeakPropertyListener
@Override
public void addListener(Observable target) {
    target.addOnPropertyChangedCallback(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);
}

/**************************************************************************************************************/
//class LiveDataListener
@Override
public void addListener(LiveData<?> target) {
    if (mLifecycleOwner != null) {
        target.observe(mLifecycleOwner, this);
    }
}

@Override
public void onChanged(@Nullable Object o) {
    ViewDataBinding binder = mListener.getBinder();
    if (binder != null) {
        binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0);
    }
}

这里可以看到ObservableFile与LiveData的区别是:LiveData需要绑定生命周期拥有者,而ObservableFile不需要。当target的属性发生变化是会调用ViewDataBinding的handleFieldChange()方法来更新UI:

//ViewDataBinding.java
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) {
        requestRebind();
    }
}

从注释看当正在进行LiveData的注册时都会触发field的改变,因此无需执行handleFieldChange()。调用虚方法onFieldChange()继续处理,该方法的真正实现在生成类ItemOrderList2BindingImpl中:

//ItemOrderList2BindingImpl.java
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
    switch (localFieldId) {
        case 0 :
            return onChangeMOrderModelTempObservable((androidx.databinding.ObservableField<java.lang.Integer>) object, fieldId);
    }
    return false;
}

private boolean onChangeMOrderModelTempObservable(androidx.databinding.ObservableField<java.lang.Integer> MOrderModelTempObservable, int fieldId) {
    if (fieldId == BR._all) {
        synchronized(this) {
                mDirtyFlags |= 0x1L;
        }
        return true;
    }
    return false;
}

// dirty flag
private  long mDirtyFlags = 0xffffffffffffffffL;
/* flag mapping
   flag 0 (0x1L): mOrderModel.tempObservable
   flag 1 (0x2L): mOrderModel
   flag 2 (0x3L): null
flag mapping end*/

在ItemOrderList2BindingImpl的实现方法中只是更新了mDirtyFlags |= 0x1L其余什么都没做,从注释可知mDirtyFlags是用来控制UI更新维度的,这里有两个维度:0x1L代表更新绑定在mOrderModel.tempObservable的UI,0x2L表示更新绑定在mOrderModel上的UI,回顾【DataBinding绑定解析——执行绑定】一节的executeBindings()方法可只UI更新的逻辑。回到ViewDataBinding的handleFieldChange()方法,当onFieldChange()方法返回true表明成功设置了mDirtyFlags可以进行UI绑定了,这是就可以调用requestRebind()方法执行UI绑定了,到此UI绑定完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值