美文网首页程序员首页投稿
Architecture Components MVVM架构演进

Architecture Components MVVM架构演进

作者: Mr云台 | 来源:发表于2017-11-27 13:36 被阅读0次

    在对MVP的架构实践中,发现写单元测试不是那么方便,因为Presenter持有了View的引用,而Mock View的 行为和方法特别的卡慢,因此只能把具体的业务逻辑再抽成一个个独立于Presenter的Logic,进而对Logic写单元测试。

    为了探索一种更好的写单测的开发架构,本人转而探索MVVM架构,Google推出的Architecture Components 加上 DataBinding可以实现一个比较好的MVVM架构。 本文拟分享解Google 2017 的 Architecture Components的几个组件,帮助读者快速理解它们,最后加上本人对于MVVM架构的一些思考,如有问题请留言指出。

    一、Architecture Components 是什么

    按照官网的概述,Architecture Components包含了一系列的组件,这些组件能帮助你设计出稳健的,可测试的,架构清晰的app。
    主要包含以下几个部分:

    • Lifecycles组件:定制能自动响应activity\Fragment生命周期的组件

    • LiveData:可被观察订阅的数据,数据发生变化能够通知 activie 态的 观察者

    • ViewModel:一种新的 保存 UI相关 数据的 组件,这种组件能够响应 activity\Fragment生命周期,当configuration发生变化,activity重新创建,它仍然活着,能够快速响应上一次获取的数据。

    • Pageing:分批加载,能够从指定数据源(比如Room数据库)分批加载数据,能配合PagedListAdapter、RecylerView实现分页加载,并且实现了==diff机制==,能够局部更新。

    • Room:Google对于sqlite的一层抽象和包装,实现了ORM,主要优点是体积小,编译检查sql语法,可测试。

    注:根据国外大神的测试结果,Room体积非常小,但是性能并没有比得上Realm,Paging和Room理解起来很容易,本文不具体去详细介绍。

    二、代码、快速了解

    1. 创建一个ViewModel,持有一个liveData
    public class MyViewModel extends ViewModel {
    
       // 创建可变LiveData
       private MutableLiveData<UserDTO> mUserDTO;
    
        public MutableLiveData<String> getCurrentUser() {
            if (mUserDTO == null) {
                mUserDTO = loadData()
            }
            return mUserDTO;
        }
    
        // 获取数据
        private MutableLiveData<UserDTO> loadData(){}
       
    }
    
    2. 使用ViewModel,观察其持有的LiveData
    public class NameActivity extends AppCompatActivity {
    
        private NameViewModel mModel;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            // 其他代码
    
            // 获取ViewModel,绑定当前的activity的生命周期
            mModel = ViewModelProviders.of(this).get(MyViewModel.class);
    
            //创建观察者
            final Observer<UserDTO> userObserver = new Observer<UserDTO>() {
                @Override
                public void onChanged(@Nullable final UserDTO user) {
                    // 更新UI
                    mNameTextView.setText(user.TrueName);
                }
            };
    
            // 绑定观察者,和当前activity的生命周期
            mModel.getCurrentUser().observe(this, userObserver);
        }
    }
    

    三、详解Lifecycle、liveData、ViewModel

    1. Lifecycle

    以往我们写一些自定义组件,总是要求activity\Fragment在销毁的时候,调用组件的响应方法,比如广播需要在onDesDroy()取消注册,这种模式有两个问题:

    • activity、Fragment的开发者会==忘记调用==自定义组件的相应方法

    • 调用自定义组件的onDesdroy()等类似的代码重复、冗余,到处分布。

    为了解决这个问题,Google设计了能响应activity\Fragment生命周期的机制,那就是Lifecycle

    Lifecycle主要包含三个部分:

    • Lifecycle 抽象类:看源码可以知道,这个类持有了activity、fragment等组件的生命周期状态,是一个被观察的对象,拥有addObserver 和 removeObserver等方法。
      [图片上传失败...(image-299d96-1511760755846)]

    • LifecycleOwner接口 : 接口的实现者默认持有一个Lifecycle,外接可以调用getLifecycle获取到它持有的Lifecycle对象。26以后,SupportActivity、fragment等组默认实现了该接口

    • LifecycleObserver接口 : Lifecycle的观察者,没有任何方法,主要依靠@OnLifecycleEvent 起效果

    public class MyObserver implements LifecycleObserver {
        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
        public void connectListener() {
            ...
        }
    
        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        public void disconnectListener() {
            ...
        }
    }
    
    myActivity.getLifecycle().addObserver(new MyObserver());
    

    如上代码所示,MyObserver组件的方法会随着myActivity的各种生命而得到调用了。

    Lifecycle是LiveData和ViewModel的基础。

    2. LiveData

    LiveData主要用于包装其他数据,使其成为可被观察的数据,LiveData主要要以下特点:

    • 只要让观察者的生命周期处于active的(STARTED or RESUMED) 才会给它发送值变更事件。

    • 当观察者结束生命周期时,LiveData会自动移除该观察者

    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            removeObserver(observer);
                return;
            }
            // immediately set active state, so we'd never dispatch anything to inactive
            // owner
            activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
        }
    
    
    • 由上可知,LiveData和观察者之间是一个双向绑定的过程,实际上,当观察者(LifecycleOwner)注册到LiveData的时候,LiveData也会在内部 初始化一个LifecycleObserver去观察它的观察者。
      //LiveData的添加观察者的方法
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
            if (owner.getLifecycle().getCurrentState() == DESTROYED) {
                // ignore
                return;
            }
            
            //LiveData会初始化一个LifecycleObserver去观察它的观察者
            LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
            
            LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
          
            if (existing != null && existing.owner != wrapper.owner) {
                throw new IllegalArgumentException("Cannot add the same observer"
                        + " with different lifecycles");
            }
            if (existing != null) {
                return;
            }
            owner.getLifecycle().addObserver(wrapper);
        }
    

    3. ViewModel

    ViewModel究竟是个什么?

    先不谈是是不是MVVM中那个ViewModel,在Android里面ViewModel可以理解成一个数据组件,用来持有UI的数据,它的生命周期图如下:
    [图片上传失败...(image-3e55d-1511760755846)]

    • 从上图可以看到,ViewModel的生命周期已经超出了activity,即使因为旋转等原因,activity重新创建,它仍然活着,只有当activity彻底finidhed的时候,它才会被回收。

    • 实际上ViewModel的生命周期和传入的Lifecycle绑定,如果传入的是activity,在activity finished之后ViewModel销毁,如果是Fragment,则 dettach后销毁。

    // 获取ViewModel,绑定当前的activity的生命周期
    mModel = ViewModelProviders.of(this).get(MyViewModel.class);
    
    ViewModel使用场景
    • 用来保存UI的数据,比传统方式会有一些优势(生命周期)

    • 用于一个activity钟的Fragment共享数据

    public class SharedViewModel extends ViewModel {
        private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
    
        public void select(Item item) {
            selected.setValue(item);
        }
    
        public LiveData<Item> getSelected() {
            return selected;
        }
    }
    
    public class MasterFragment extends Fragment {
        private SharedViewModel model;
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
            itemSelector.setOnClickListener(item -> {
                model.select(item);
            });
        }
    }
    
    public class DetailFragment extends Fragment {
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
            model.getSelected().observe(this, { item ->
               // Update the UI.
            });
        }
    }
    
    • 和DataBinding机制联合起来用,省去数据和View的相互绑定代码。

    • 注意,ViewModel应为生命周期比较长,所以不应该以任何形式持有一个View\Lifecycle\activity,以免造成内存泄漏。

    四、Architecture Components带来的MVVM架构

    1. 先看下 MVC和MVP和MVVM的概念 (图片来自阮一峰的博客)
    MVC架构
    image

    非常传统,不多解释了

    MVP架构
    image

    相比MVC,断开了View和Model的引用,更方便重构View和Model层,在实践中,一般一个View(Activity或者Fragment)对应一个Presenter,Presenter和View相互抽象出接口供对方调用。

    MVVM架构
    image

    MVVM相比MVP Presenter换成了 ViewModel,ViewModel和View变成了双向绑定,View的变动,自动反映在 ViewModel,反之亦然。

    这种双向绑定,比较清晰的例子就是Vue.js的 v-model指令,实现了数据和View的双向绑定。

    MVVM有什么优势呢?

    1. 先谈谈数据驱动的概念

    支持MVVM的人(比如Vue.js的支持者)都会养成一个概念,view是由给与它的数据决定的,view的变化也应该是数据的变化引起的。

    即使是showLoading这种看似无关的view的状态,也应该转化为数据model的一个Bool值——isShowLoding,view根据model中的isShowLoding的值,决定是否显示Loading界面。

    (如果怕麻烦,直接使用回调,View调用ViewModel的方法,同时传入回调,获取到数据后,在回调里关闭Loading界面,这样ViewModel也不会持有View的引用. 因为Kotlin写回调太方便了,这种方式也不错,但是不注意不要陷入回调地狱)

    2. 断开引用

    Architecture Components的LiveData机制,可以完全实现上述描述的数据驱动概念,View观察ViewModel(或者叫做Presenter,命名无所谓)中的LiveData数据,根据数据的变化而呈现不同的UI。

    ViewModel 不需要关心View是怎么样的,不需要调用View的方法,因此也不需要再持有View的引用,这种架构更方便重构、也不需要再从Presenter抽出一个个Logic(以前是为了重用和写单元测试),我们可以直接针对ViewModel写单元测试。

    3. ViewModel重用性更好

    ViewModel相当于一个高内聚的数据获取器,不再局限于于被哪个View使用,因此它的可用性更好,界面A可以用,界面B也可以用。

    3. 更少的代码

    MVVM一般会由框架层实现数据和View的双向绑定,因此代码会更加精简。

    好了,以前是本人对于Architecture Components组件带来的一些关于架构的思考,不再上代码了,相信实践MVP和痛苦于写单元测试的人会有更深的理解。

    非常传统,不多解释了

    相关文章

      网友评论

        本文标题:Architecture Components MVVM架构演进

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