美文网首页android mvvm
Android MVVM 解读 3. Android MVVM

Android MVVM 解读 3. Android MVVM

作者: yqpan1991 | 来源:发表于2020-02-21 02:26 被阅读0次

    2.5 Repository

    没有过多需要描述的, 唯一需要区别的是返回值采用LiveData的方式, Demo 案例 来自google

    class UserRepository {
       private val webservice: Webservice = TODO()
       // ...
       fun getUser(userId: String): LiveData<User> {
           // This isn't an optimal implementation. We'll fix it later.
           val data = MutableLiveData<User>()
           webservice.getUser(userId).enqueue(object : Callback<User> {
               override fun onResponse(call: Call<User>, response: Response<User>) {
                   data.value = response.body()
               }
               // Error case is left out for brevity.
               override fun onFailure(call: Call<User>, t: Throwable) {
                   TODO()
               }
           })
           return data
       }
    }
    
    final-architecture.png

    根据此图, Repository的部分, 因为涉及到可能来自本地的数据源,或者来自网络的数据源,甚至会存在不同的数据有逻辑顺序的数据源,例如,先获取本地数据,在本地获取成功后,再次获取到远端的数据, 因而数据层,负责这些部分.

    2.6 DataBinding

    Google 官方说明: Data Binding Library

    DataBinding Communication

    DataBinding Communication.png

    DataBinding Init Sequence

    DataBinding Init Sequence.png

    DataBindingUtil.sMapper的实现

    /**
     * Inflates a binding layout and returns the newly-created binding for that layout.
     * This uses the DataBindingComponent set in
     * {@link #setDefaultComponent(DataBindingComponent)}.
     * <p>
     * Use this version only if <code>layoutId</code> is unknown in advance. Otherwise, use
     * the generated Binding's inflate method to ensure type-safe inflation.
     *
     * @param <T> Type of the generated binding class.
     * @param inflater The LayoutInflater used to inflate the binding layout.
     * @param layoutId The layout resource ID of the layout to inflate.
     * @param parent Optional view to be the parent of the generated hierarchy
     *               (if attachToParent is true), or else simply an object that provides
     *               a set of LayoutParams values for root of the returned hierarchy
     *               (if attachToParent is false.)
     * @param attachToParent Whether the inflated hierarchy should be attached to the
     *                       parent parameter. If false, parent is only used to create
     *                       the correct subclass of LayoutParams for the root view in the XML.
     * @return The newly-created binding for the inflated layout or <code>null</code> if
     * the layoutId wasn't for a binding layout.
     * @throws InflateException When a merge layout was used and attachToParent was false.
     * @see #setDefaultComponent(DataBindingComponent)
     */
    // @Nullable don't annotate with Nullable. It is unlikely to be null and makes using it from
    // kotlin really ugly. We cannot make it NonNull w/o breaking backward compatibility.
    public static <T extends ViewDataBinding> T inflate(@NonNull LayoutInflater inflater,
            int layoutId, @Nullable ViewGroup parent, boolean attachToParent) {
        return inflate(inflater, layoutId, parent, attachToParent, sDefaultComponent);
    }
    
    
    /**
     * Inflates a binding layout and returns the newly-created binding for that layout.
     * <p>
     * Use this version only if <code>layoutId</code> is unknown in advance. Otherwise, use
     * the generated Binding's inflate method to ensure type-safe inflation.
     *
     * @param <T> Type of the generated binding class.
     * @param inflater The LayoutInflater used to inflate the binding layout.
     * @param layoutId The layout resource ID of the layout to inflate.
     * @param parent Optional view to be the parent of the generated hierarchy
     *               (if attachToParent is true), or else simply an object that provides
     *               a set of LayoutParams values for root of the returned hierarchy
     *               (if attachToParent is false.)
     * @param attachToParent Whether the inflated hierarchy should be attached to the
     *                       parent parameter. If false, parent is only used to create
     *                       the correct subclass of LayoutParams for the root view in the XML.
     * @param bindingComponent The DataBindingComponent to use in the binding.
     * @return The newly-created binding for the inflated layout or <code>null</code> if
     * the layoutId wasn't for a binding layout.
     * @throws InflateException When a merge layout was used and attachToParent was false.
     */
    // @Nullable don't annotate with Nullable. It is unlikely to be null and makes using it from
    // kotlin really ugly. We cannot make it NonNull w/o breaking backward compatibility.
    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);
    }
    

    在Fragment中,采用inflate的方式时, 我们看到最终是需要通过变量sMapper来寻找到的, 而sMapper的实例是: DataBinderMapperImpl, 这块在源码中我们没有看到, 实现在哪里? 其实是我们在开启了
    dataBinding {
    enabled = true
    }
    时, 代码会自动生成,

    在我们的application的工程(一般名字为app)中查找实现部分

        package android.databinding;
        public class DataBinderMapperImpl extends MergedDataBinderMapper {
        DataBinderMapperImpl() {
          addMapper(new com.test.app.DataBinderMapperImpl());// 将我们的application的DataBinderMapperImpl 注册
        }
        }
    

    继续查看实现的com.test.app.DataBinderMapperImpl的实现

      package com.test.app;
    
      public class DataBinderMapperImpl extends DataBinderMapper {
      private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(0);
    
      static {
      }
    
      @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");
         }
       }
       return null;
      }
    
      @Override
      public ViewDataBinding getDataBinder(DataBindingComponent component, View[] views, int layoutId) {
       if(views == null || views.length == 0) {
         return null;
       }
       int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
       if(localizedLayoutId > 0) {
         final Object tag = views[0].getTag();
         if(tag == null) {
           throw new RuntimeException("view must have a tag");
         }
         switch(localizedLayoutId) {
         }
       }
       return null;
      }
    
      @Override
      public int getLayoutId(String tag) {
       if (tag == null) {
         return 0;
       }
       Integer tmpVal = InnerLayoutIdLookup.sKeys.get(tag);
       return tmpVal == null ? 0 : tmpVal;
      }
    
      @Override
      public String convertBrIdToString(int localId) {
       String tmpVal = InnerBrLookup.sKeys.get(localId);
       return tmpVal;
      }
    
      @Override
      public List<DataBinderMapper> collectDependencies() {
       ArrayList<DataBinderMapper> result = new ArrayList<DataBinderMapper>(1);
       result.add(new com.test.module.DataBinderMapperImpl());// 将Application的依赖的module注册进来
       return result;
      }
    
      private static class InnerBrLookup {
       static final SparseArray<String> sKeys = new SparseArray<String>(14);
    
       static {
         sKeys.put(0, "_all");
         sKeys.put(1, "image");
         sKeys.put(2, "item");
         sKeys.put(3, "onClick");
         sKeys.put(4, "data");
         sKeys.put(5, "visibility");
         sKeys.put(6, "vm");
         sKeys.put(7, "text");
         sKeys.put(8, "stepVm");
         sKeys.put(9, "type");
         sKeys.put(10, "retry");
         sKeys.put(11, "refundOnly");
         sKeys.put(12, "refundAndReturn");
       }
      }
    
      private static class InnerLayoutIdLookup {
       static final HashMap<String, Integer> sKeys = new HashMap<String, Integer>(0);
    
       static {
       }
      }
      }
    

    通过上述的代码的分析得知,

    1. DataBindingUtil.sMapper的具体的实现,是通过代码生成器生成的
    2. 此mapper是在application的工程中,生成的类
    3. 此类,一般会有一个注册的mapper,为当前application的mapper
    4. application的mapper中,注册了其关联工程的mapper(这也是为什么, 我们的build.gradle中的dataBinding的enabled = true, 要从依赖的源头到application的工程一路注册,因为mapper,也是逐级传递的)
    5. 每个开启了dataBinding的enable=true的模块都有自己的DataBinderMapperImpl, 负责的是自己模块的dataBinding的处理和负责搜集子模块的DataBinderMapperImpl
    6. 通过这一系列的DataBinderMapperImpl, 在调用 DataBindUtils的方法
      static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
        int layoutId) {
    return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
    

    }
    便可以找到对应的ViewDataBinding,并且初始化.

    相关文章

      网友评论

        本文标题:Android MVVM 解读 3. Android MVVM

        本文链接:https://www.haomeiwen.com/subject/pujqqhtx.html