MVP 与 MVVM 优缺点总结

项目经验,如需转载,请注明做者:Yuloran (t.cn/EGU6c76)java

前言

主要阐述 Android App 架构之 MVP 与 MVVM 的优势与痛点,不介绍具体的实现方式。由于 MVP 架构简单,无需介绍。而 MVVM 架构相对复杂,核心是 LifecycleOwner、LifecycleObserver、LifecycleRegistry 组件,在此之上,Google 还开发了 DataBinding、ViewModel、LiveData 以实现完整的 MVVM 架构。相关组件已收纳至 JetPack Architecture 中。具体实践可参考本人的开源项目 wanandroid_java,这是一个 MVVM 架构的最小化实践,便于理解以及探索 MVVM 架构背后的实现原理。android

MVP

实际上按照代码在 App 中所处位置,描述为 VPM 更合适,即 View -> Presenter -> Model:git

这个架构的核心就是 View 与 Presenter 都被抽象成了接口,是面向接口编程的一个实践。由于面向接口,因此实现了依赖隔离,即无需知道具体的 Class 类型。github

优势

  • 只需定义好 View 与 Presenter 的接口,便可实现 UI 与业务逻辑独立开发,可由不一样的开发团队分别实现
  • 业务逻辑只在 Presenter 中进行维护,遵循了单一职责类的设计原则,提高了代码的可维护性
  • 接口请求及缓存策略只在 Model 中进行维护,同 Presenter 同样,遵循了单一职责类的设计原则,提高了代码的可维护性
  • 避免了 MVC 架构中,Controller 类过于庞大的问题。MVP 架构中,View、Presenter、Model 类的体积都不会太大,提高了代码的可读性与可维护性

痛点

其实这并非 MVP 架构的痛点,而是整个 Android App 开发的痛点,那就是对 UI 的操做必须在 Activity 与 Fragment 的生命周期以内,更细致一点,最好在 onStart() 以后 onPause()以前,不然极其容易出现各类异常。而 MVP 架构中,Presenter 对 Activity 与 Fragment 的生命周期是无感知的,因此咱们须要手动添加相应的生命周期方法,并进行特殊处理,以免出现异常或内存泄露。编程

MVVM

实际上按照代码在 App 中所处位置,描述为 VVMM 更合适,即 View -> ViewModel-> Model:缓存

这个架构的核心就是 ViewModel 和 LiveData。ViewModel 的做用是保证当设备因配置改变而从新建立 FragmentActivity(目前 ViewModel 仅支持 FragmentActivity 和 Fragment) 时,数据也不会丢失。LiveData 的做用是保证只在 FragmentActivity 或 Fragment 的生命周期状态为 [onStarted, onResumed] 时,回调 onChanged(T data),因此咱们能够在 onChanged() 中安全的更新 UI。下面简单介绍源码中是如何实现的:安全

ViewModel 不重建原理

  1. 保存 ViewModel 实例:
/** * Retain all appropriate fragment state. You can NOT * override this yourself! Use {@link #onRetainCustomNonConfigurationInstance()} * if you want to retain your own state. */
    @Override
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

        if (fragments == null && mViewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = mViewModelStore;
        nci.fragments = fragments;
        return nci;
    }
复制代码
  1. 恢复 ViewModel 实例:
/** * Perform initialization of all fragments. */
    @SuppressWarnings("deprecation")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        mFragments.attachHost(null /*parent*/);

        super.onCreate(savedInstanceState);

        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null && nc.viewModelStore != null && mViewModelStore == null) {
            mViewModelStore = nc.viewModelStore;
        }

        // omit the irrelevant codes
        ...
    }
复制代码

由上述代码可见,在 onCreate() 中,若是 getLastNonConfigurationInstance() 不为 null,则将其中的 viewModelStore 恢复为成员变量 mViewModelStore,而 mViewModelStore 内部缓存了 ViewModel 的实例:网络

public class ViewModelStore {
    // key是固定前缀加ViewModel实现类的类名,value是实现类实例
    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    /** * Clears internal storage and notifies ViewModels that they are no longer used. */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}
复制代码

LiveData 安全更新原理

  1. 注册 observer 并监听 LifecycleOwner 生命周期改变。第一个参数是 LifecycleOwner (一个接口,表示一个类拥有生命周期),目前的实现类有 FragmentActivity 和 Fragment (注:文中 Fragment 皆指 support library 中的 Fragment,目前 Google 已经迁移至 androidx.* 包下,android.app.* 包下的 Fragment 已经被标记为 Deprecated,不推荐使用)
@MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        // 将入参 observer 封装为 LifecycleBoundObserver,这是很常见的写法,也是设计原则之一:多用复合、少用继承
        // 为便于区分,笔者将入参 observer 称为 inner observer
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        // 注册到 Lifecycle 中,以监听生命周期变化
        owner.getLifecycle().addObserver(wrapper);
    }
复制代码
  1. 可以监听生命周期改变的 LifecycleBoundObserver
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
        @NonNull
        final LifecycleOwner mOwner;

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
            mOwner = owner;
        }

        /** * 只有 STARTED 或者 RESUMED 才会返回 true */
        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        /** * 生命周期改变时,都会回调 */
        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            // destroy 时,移除 inner observer
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());
        }

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }
复制代码

父类:架构

private abstract class ObserverWrapper {
        final Observer<? super T> mObserver;
        boolean mActive;
        int mLastVersion = START_VERSION;

        ObserverWrapper(Observer<? super T> observer) {
            mObserver = observer;
        }

        abstract boolean shouldBeActive();

        boolean isAttachedTo(LifecycleOwner owner) {
            return false;
        }

        void detachObserver() {
        }

        void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            // immediately set active state, so we'd never dispatch anything to inactive
            // owner
            mActive = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            if (wasInactive && mActive) {
                onActive();
            }
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive();
            }
            if (mActive) {
                dispatchingValue(this);
            }
        }
    }
复制代码

咱们看到,每次生命周期改变时,都会回调 onStateChanged()。若是是 destroy 就移除 inner observer,不然回调 activeStateChanged(shouldBeActive()) 将 shouldBeActive() 的返回值赋给 mActive。而 shouldBeActive() 只有在生命周期为 STARTED 或者 RESUMED 时才返回 true,也只有 mActive 为 true时,才会回调 onChanged:app

private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
        //
        // we still first check observer.active to keep it as the entrance for events. So even if
        // the observer moved to an active state, if we've not received that event, we better not
        // notify for a more predictable notification order.
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);
    }
复制代码

由于 LiveData 支持非 UI 线程更新 mData ( postValue() 方法),因此引入了版本号以解决 ABA 问题。

优势

  • ViewModel:因设备配置改变致使 Activity 重建时,无需从 Model 中再次加载数据,减小了 IO 操做
  • LiveData:更新 UI 时,不用再关注生命周期问题
  • Data Binding: 能够有效减小模板代码的编写,并且目前已经支持双向绑定 (注意:不是全部的 UI 都须要使用 Data Binding,虽然经过 @BindingAdapter 咱们真的能够“随心所欲”,最好仍是只用于须要绑定 Bean 类的布局)

缺点

  1. 实际编写 App 过程当中,在展现最终数据以前,须要展现过渡 UI,好比一个进度条、一个炫酷的动画、加载失败时展现异常提示等,这些能够称之为 ViewState。在 MVP 架构中,这些状态的更新被定义在 View 接口中。可是 MVVM 架构没有涉及。因此咱们须要自行封装一个 ViewData 类,用于封装数据类和 ViewState,就像这样:
/** * [UI State: init->loading->loadSuccess|loadFailure] * <p> * Author: Yuloran * Date Added: 2018/12/20 20:59 * * @param <T> UI须要的数据类型 * @since 1.0.0 */
public class BaseViewData<T> {
    @NonNull
    private ViewState viewState;

    private T viewData;

    public BaseViewData(@NonNull ViewState viewState) {
        Objects.requireNonNull(viewState, "viewState is null!");
        this.viewState = viewState;
    }

    public BaseViewData(@NonNull T viewData) {
        this.viewState = ViewState.LOAD_SUCCESS;
        this.viewData = viewData;
    }

    @Nullable
    public T getViewData() {
        return viewData;
    }

    public void setViewData(@NonNull T data) {
        this.viewData = data;
    }

    @NonNull
    public ViewState getViewState() {
        return viewState;
    }

    public void setViewState(ViewState viewState) {
        this.viewState = viewState;
    }
}
复制代码

这样,即可以经过 LiveData 方便的更新 UI了。

  1. 因为 LiveData 使用的是观察者模式,因此要避免产生循环调用。好比接口请求失败时,经过 LiveData 回调请求失败,在回调中发起新的接口请求。若是接口请求一直失败,又没有作特殊处理的话,就产生了循环调用,这会占用大量的 CPU 资源,下降 App 的性能。

  2. LiveData 在每次收到生命周期改变的回调时,只要 inner observer 的 mActive 为 true,而且 mLastVersion != mVersion,都会回调 onChanged(),这在某些场景下会有问题,好比在 onChanged() 中弹出对话框,或者跳转至其它页面。想象一下这个场景:Activity 因设备配置改变而重建了,那么当 Activity 走到 onStart() 时,LiveData 就会回调 onChanged(),这是由于 Activity 重建后,虽然 ViewModel 没有重建,可是 LiveData 的 inner observer 倒是从新注册的,因此这个 observer 的 mLastVersion != mVersion,根据上文源码可知,此时必定会回调 onChanged,进而致使错误的弹出了对话框或错误的跳转至了其它页面,使用 Google samples 里面的 SingleLiveEvent 能够规避这个问题。

  3. LiveData 自己是没有 public 方法的,因此咱们应该使用其子类 MutableLiveData。这样设计,咱们就能够在 Model 中使用 MutableLiveData,在 ViewModel 中,只对 View 提供 LiveData,避免 View 去更新 LiveData。本人也写了一个类 SafeMutableLiveData,用于自动调用 setValue() 仍是 postValue(),同时为 getValue() 提供一个默认值。

  4. 有些场景,好比网络连通性监听,仅须要在确实发生改变时,回调 onChanged(),可是目前 LiveData 没法实现。在 onCreate() 中,给 LiveData 添加 observer 后,只要走到 onStart(),必然会回调 onChanged(),并将上一次的 mData 发送过来,实际上此时并未产生网络链接改变的动做。因此,这种场景还须要特殊处理。

结语

架构设计的目的是提高代码的可读性、可维护性,而不是过分提高代码复杂性,更不是随意引入某某框架。Google 官方也说了,万能架构是不存在的。若是是已经很是稳定的项目,则没有必要从新架构。只不过做为开发人员,仍是要保持对新技术的敏感性。

  1. Lifecycle.State

lifecycle-states

  1. ViewModel Lifecycle

viewmodel-lifecycle

图片来自 Google Android 官网

相关文章
相关标签/搜索