2.2.2.Architecture components_data binding2_源码分析

参考

https://developer.android.com/topic/libraries/data-binding

https://blog.csdn.net/qiang_xi/article/details/74347880

获取绑定对象解析

DataBindingUtil

  • l DataBindingUtil.setContentView(@NonNull Activity activity, int layoutId)
  • l DataBindingUtil.bind(View root)
  • l DataBindingUtil.inflate(LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent, boolean attachToParent)
  • l DataBindingUtil.findBinding(View view)

ActivityMain2Binding生成的绑定类

  • l ActivityMain2Binding.inflate(LayoutInflater inflater)
  • l ActivityMain2Binding.bind(View view)

以上静态方法都可以获取绑定对象,但有什么区别呢,这就需要去看看源码怎么实现的了。

先说一下databinding帮我们做了什么:

当我们在xml中写完布局绑定后,编译后会自动生成几个java和处理过的布局xml,具体路径为build里的:

wps66 wps67

  • l 对于处理后的布局xml,里边把除了普通布局外的东西都删除(就是把data,layout去除,只留下布局,也会把表达式也去除),
  • l 同时在每个view上设置了tag(所以不要在布局xml中自己设置tag),

其中对布局根view设置的tag是layout/xml名字_0,例如布局是databinding1.xml,那么android:tag="layout/databinding1_0"。

当然如果你放的布局xml在其他layout文件夹(比如layout-land)那么前边的layout就是那个文件夹名。

布局根view设置的tag是为了在后边获取绑定对象的。

DataBindingUtil

DataBindingUtil.setContentView

这个是个方便的方法,setContentView后同时获取绑定对象。

public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
        int layoutId, @Nullable DataBindingComponent bindingComponent) {
    activity.setContentView(layoutId);
    View decorView = activity.getWindow().getDecorView();
    ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
    return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}
private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component,
        ViewGroup parent, int startChildren, int layoutId) {
    final int endChildren = parent.getChildCount();
    final int childrenAdded = endChildren - startChildren;
    if (childrenAdded == 1) {
        final View childView = parent.getChildAt(endChildren - 1);
        return bind(component, childView, layoutId);
    } else {
        final View[] children = new View[childrenAdded];
        for (int i = 0; i < childrenAdded; i++) {
            children[i] = parent.getChildAt(i + startChildren);
        }
        return bind(component, children, layoutId);
    }
}
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
        int layoutId) {
    return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}

com.example.jetpackdemo.DataBinderMapperImpl

private static final int LAYOUT_ACTIVITYMAIN2 = 1;

private static final int LAYOUT_ACTIVITYMAIN3 = 2;

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

static {
  INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.jetpackdemo.R.layout.activity_main2, LAYOUT_ACTIVITYMAIN2);
  INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.jetpackdemo.R.layout.activity_main3, LAYOUT_ACTIVITYMAIN3);
}

@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_ACTIVITYMAIN2: {
        if ("layout/activity_main2_0".equals(tag)) {
          return new ActivityMain2BindingImpl(component, view);
        }
        throw new IllegalArgumentException("The tag for activity_main2 is invalid. Received: " + tag);
      }
      case  LAYOUT_ACTIVITYMAIN3: {
        if ("layout/activity_main3_0".equals(tag)) {
          return new ActivityMain3BindingImpl(component, view);
        }
        throw new IllegalArgumentException("The tag for activity_main3 is invalid. Received: " + tag);
      }
    }
  }
  return null;
}

可以看出,最终是通过获取view的tag(这个tag就是前边说的处理后的 xml自动加上的tag),根据tag来创建具体的绑定对象的。

生成的绑定类的构造函数

private ActivityMain2BindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
    super(bindingComponent, root, 0
        , (android.widget.LinearLayout) bindings[0]
        , (android.widget.TextView) bindings[3]
        , (android.widget.TextView) bindings[2]
        , (android.widget.TextView) bindings[4]
        );
    this.mboundView0 = (com.example.jetpackdemo.databinding.ActivityMain3Binding) bindings[6];
    setContainedBinding(this.mboundView0);
    this.mboundView1 = (android.widget.TextView) bindings[1];
    this.mboundView1.setTag(null);
    this.mboundView5 = (android.widget.TextView) bindings[5];
    this.mboundView5.setTag(null);
    this.root.setTag(null);
    this.tvAge.setTag(null);
    this.tvName.setTag(null);
    this.tvSex.setTag(null);
    setRootTag(root);
    // listeners
    mCallback1 = new com.example.jetpackdemo.generated.callback.OnClickListener(this, 1);
    invalidateAll();
}
protected void setRootTag(View view) {
    view.setTag(R.id.dataBinding, this);
}

可以看到创建后,就把root的tag设置为null,并设置了view.setTag(R.id.dataBinding, this),就是做了个缓存,以后从此view就可以直接获取绑定对象。

DataBindingUtil.bind

public static <T extends ViewDataBinding> T bind(@NonNull View root,
        DataBindingComponent bindingComponent) {
    T binding = getBinding(root);
    if (binding != null) {
        return binding;
    }
    Object tagObj = root.getTag();
    if (!(tagObj instanceof String)) {
        throw new IllegalArgumentException("View is not a binding layout");
    } else {
        String tag = (String) tagObj;
        int layoutId = sMapper.getLayoutId(tag);
        if (layoutId == 0) {
            throw new IllegalArgumentException("View is not a binding layout. Tag: " + tagObj);
        }
        return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
    }
}

DataBindingUtil.getBinding最终会调用ViewDataBinding的getBinding

static ViewDataBinding getBinding(View v) {
    if (v != null) {
        return (ViewDataBinding) v.getTag(R.id.dataBinding);
    }
    return null;
}

如果没有找到缓存,sMapper.getLayoutId(tag)

com.example.jetpackdemo.DataBinderMapperImpl

@Override
public int getLayoutId(String tag) {
  if (tag == null) {
    return 0;
  }
  Integer tmpVal = InnerLayoutIdLookup.sKeys.get(tag);
  return tmpVal == null ? 0 : tmpVal;
}

private static class InnerLayoutIdLookup {
  static final HashMap<String, Integer> sKeys = new HashMap<String, Integer>(2);

  static {
    sKeys.put("layout/databinding1_0", com.example.jetpackdemo.R.layout.databinding1);
    sKeys.put("layout/databinding2_0", com.example.jetpackdemo.R.layout.databinding2);
  }
}

从上边可以看出:

  • l 先从view上获取key为R.id.dataBinding的tag,也就是获取的缓存。
  • l 如果没有(从上边生成的绑定类的构造函数可知,说明此时没有创建过此绑定对象),那么就从view身上获取tag,

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

可以看到这种方式,是在xml还没有解析过时才用的,一般用在fragment,list view的adapter等里。

DataBindingUtil.getBinding()

public static <T extends ViewDataBinding> T getBinding(@NonNull View view) {
    return (T) ViewDataBinding.getBinding(view);
}
static ViewDataBinding getBinding(View v) {
    if (v != null) {
        return (ViewDataBinding) v.getTag(R.id.dataBinding);
    }
    return null;
}

也就是找缓存,找不到就返回null。

DataBindingUtil.findBinding()

检索负责给定View的绑定。 如果view不是绑定布局的根,则将在其父级中搜索绑定。 如果没有绑定,则将返回null。

也就是说会向上搜索。

public static <T extends ViewDataBinding> T findBinding(@NonNull View view) {
    while (view != null) {
        ViewDataBinding binding = ViewDataBinding.getBinding(view);
        if (binding != null) {
            return (T) binding;
        }
        Object tag = view.getTag();
        if (tag instanceof String) {
            String tagString = (String) tag;
            if (tagString.startsWith("layout") && tagString.endsWith("_0")) {
                final char nextChar = tagString.charAt(6);
                final int slashIndex = tagString.indexOf('/', 7);
                boolean isUnboundRoot = false;
                if (nextChar == '/') {
                    // only one slash should exist
                    isUnboundRoot = slashIndex == -1;
                } else if (nextChar == '-' && slashIndex != -1) {
                    int nextSlashIndex = tagString.indexOf('/', slashIndex + 1);
                    // only one slash should exist
                    isUnboundRoot = nextSlashIndex == -1;
                }
                if (isUnboundRoot) {
                    // An inflated, but unbound layout
                    return null;
                }
            }
        }
        ViewParent viewParent = view.getParent();
        if (viewParent instanceof View) {
            view = (View) viewParent;
        } else {
            view = null;
        }
    }
    return null;
}

生成的绑定类ActivityMain2Binding

ActivityMain2Binding.inflate(LayoutInflater inflater)

public static ActivityMain2Binding inflate(@NonNull LayoutInflater inflater) {
  return inflate(inflater, DataBindingUtil.getDefaultComponent());
}
public static ActivityMain2Binding inflate(@NonNull LayoutInflater inflater,
    @Nullable Object component) {
  return ViewDataBinding.<ActivityMain2Binding>inflateInternal(inflater, R.layout.activity_main2, null, false, component);
}

可以看出其实每个生成的绑定类,都是知道自己是和哪个布局进行绑定的。

ViewDataBinding

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

接着就会走DataBindingUtil.inflate()

ActivityMain2Binding.bind(View view)

public static ActivityMain2Binding bind(@NonNull View view) {
  return bind(view, DataBindingUtil.getDefaultComponent());
}
public static ActivityMain2Binding bind(@NonNull View view, @Nullable Object component) {
  return (ActivityMain2Binding)bind(component, view, R.layout.activity_main2);
}

ViewDataBinding

protected static ViewDataBinding bind(Object bindingComponent, View view, int layoutId) {
    return DataBindingUtil.bind(
            checkAndCastToBindingComponent(bindingComponent),
            view,
            layoutId);
}
原文地址:https://www.cnblogs.com/muouren/p/12368516.html