DataBinding源码分析

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

总结 

整个反向绑定的流程下来其实就是:

  1. @InverseBindingMethod(type = CompoundButton.class, attribute = "android:checked",event="checkedAttrChanged",method="isChecked" )                                   定义需要反向绑定的属性(checked),并配置event(checkedAttrChanged)和method(isChecked)。
  2. @BindingAdapter(value = {"android:onCheckedChanged","android:checkedAttrChanged"},requireAll = false)  系统会自动根据event找到对应的方法(setListeners),配置好调用时机。setListeners中调用InverseBindingListener.onchange()
  3. 在layout中使用双向绑定android:checked="@={user.checked}。
  4. 自动在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之后,这些冗余统统可以得到简化,从而只需要专注于业务逻辑了。

------------------------------欢迎多多批评指正

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dinding是一种通过机制,将代码中的数据和XML(UI)进行绑定的技术。它允许双方都能对数据进行操作,并在数据发生变化时自动刷新数据。 在Android中,我们可以使用DataBindingUtil类的方法来创建Databinding实例,即通过DataBindingUtil.inflate(LayoutInflater, layoutId, parent, attachToParent)或DataBindingUtil.bindTo(viewRoot, layoutId)方法来得到绑定对象。 Databinding主要解决了Model层与View交互的问题,并且现在也支持双向绑定。它的优势在于可以简化代码,使得数据与UI更紧密地结合。我们可以通过一个登录的例子来进一步感受Databinding的优势。你可以下载并查看一个登录的Demo,或者自己编写相关的例子来深入理解Databinding的用法和好处。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [DataBinding详解](https://blog.csdn.net/peiyuWang_2015/article/details/76945081)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [DataBinding使用教程详解](https://blog.csdn.net/zhangwude2301/article/details/80069972)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值