内容简介:主要阐述 Android App 架构之 MVP 与 MVVM 的优点与痛点,不介绍具体的实现方式。因为 MVP 架构简单,无需介绍。而 MVVM 架构相对复杂,核心是 LifecycleOwner、LifecycleObserver、LifecycleRegistry 组件,在此之上,Google 还开发了 DataBinding、ViewModel、LiveData 以实现完整的 MVVM 架构。相关组件已收纳至JetPack Architecture 中。具体实践可参考本人的开源项目实际上按照代码在
主要阐述 Android App 架构之 MVP 与 MVVM 的优点与痛点,不介绍具体的实现方式。因为 MVP 架构简单,无需介绍。而 MVVM 架构相对复杂,核心是 LifecycleOwner、LifecycleObserver、LifecycleRegistry 组件,在此之上,Google 还开发了 DataBinding、ViewModel、LiveData 以实现完整的 MVVM 架构。相关组件已收纳至JetPack Architecture 中。具体实践可参考本人的开源项目 wanandroid_java ,这是一个 MVVM 架构的最小化实践,便于理解以及探索 MVVM 架构背后的实现原理。
MVP
实际上按照代码在 App 中所处位置,描述为 VPM 更合适,即 View -> Presenter -> Model:
这个架构的核心就是 View 与 Presenter 都被抽象成了接口,是面向接口编程的一个实践。因为面向接口,所以实现了依赖隔离,即无需知道具体的 Class 类型。
优点
- 只需定义好 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 不重建原理
- 保存 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; } 复制代码
- 恢复 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 安全更新原理
- 注册 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); } 复制代码
- 能够监听生命周期改变的 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:
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 类的布局)
缺点
- 实际编写 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了。
-
由于 LiveData 使用的是观察者模式,所以要避免产生循环调用。比如接口请求失败时,通过 LiveData 回调请求失败,在回调中发起新的接口请求。如果接口请求一直失败,又没有做特殊处理的话,就产生了循环调用,这会占用大量的 CPU 资源,降低 App 的性能。
-
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 可以规避这个问题。
-
LiveData 本身是没有 public 方法的,所以我们应该使用其子类 MutableLiveData。这样设计,我们就可以在 Model 中使用 MutableLiveData,在 ViewModel 中,只对 View 提供 LiveData,避免 View 去更新 LiveData。本人也写了一个类 SafeMutableLiveData ,用于自动调用 setValue() 还是 postValue(),同时为 getValue() 提供一个默认值。
-
有些场景,比如网络连通性监听,仅需要在确实发生改变时,回调 onChanged(),但是目前 LiveData 无法实现。在 onCreate() 中,给 LiveData 添加 observer 后,只要走到 onStart(),必然会回调 onChanged(),并将上一次的 mData 发送过来,实际上此时并未产生网络连接改变的动作。所以,这种场景还需要特殊处理。
结语
架构设计的目的是提升代码的可读性、可维护性,而不是过度提升代码复杂性,更不是随意引入某某框架。Google 官方也说了,万能架构是不存在的。如果是已经非常稳定的项目,则没有必要重新架构。只不过作为开发人员,还是要保持对新技术的敏感性。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。