zoukankan      html  css  js  c++  java
  • 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);
    }
  • 相关阅读:
    cmd 新建空文件
    查看Linux版本
    centos7时间调整
    正确卸载vs2015及以前版本方式
    vs2017,vs2019 无法连接到Web服务器“IIS Express”
    .netcore开发环境和服务器注意事项
    .netcore 网站启动后 502.5
    CentOS7开机报错piix4_smbus ****host smbus controller not enabled
    centos7 升级系统后,启动界面出现多个选项
    .gitkeep文件
  • 原文地址:https://www.cnblogs.com/muouren/p/12368516.html
Copyright © 2011-2022 走看看