聊聊 MVP 的缺点

2,984 阅读3分钟

设计 MVP 的目的:

  • 解锁 V 层的臃肿

极差体验:

1、继承
继承方式真的太糟糕了,BaseMVPActivity 本身是一种下沉到 lib 的通用类,但上层的业务层需要采用 BaseActivity 的方式来实现基础统计和埋点,意味着,我要么把 BaseMVPActivity 提到业务层,然后继承 BaseActivity,或是把 BaseActivity 下沉到 lib,供 BaseMVPActivity 继承,但我的业务统计就没办法用了。

public abstract class BaseMVPActivity<P extends MVPBasePresenter> extends BaseActivity implements MVPBaseView

2、赋予 P 层生命周期感知

    private P presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (presenter == null) {
            presenter = createPresenter();
        }
        presenter.attach((V) this);
    }
    
    @Override
    protected void onDestroy() {
        presenter.detach();
        super.onDestroy();
    }

这种方式优缺点都有:
优点是:V 层在结束页面时,无需关心数据的释放动作
缺点是:P 层在 detach 时操作了 M 层的耗时任务,会直接导致 V 层 ANR

我觉得,P 层不应该具有生命周期的感知能力

3、糟糕的接口约束
V 层调用 P 层会制定一层接口来约束当前可调用的方法,P 层在完成任务动作时,也会通过约束接口将数据回调回给 V 层,接口制定如下:

public interface HandleContract {
    interface View extends BaseView {
        void startWayPointSuccess();
        void pauseWayPointSuccess();
        void resumeWayPointSuccess();
        void clearWayPointSuccess();
        void stopWayPointSuccess();
    }
    interface Present extends BasePresenter {
        void startWayPointMission();
        void pauseWayPointMission();
        void resumeWayPointMission();
        void clearWayPointMission();
        void stopWayPointMission();
    }
}

之前一直认为,通过接口约束来实现 V 与 P 的调用模板,可以很清晰的明白双方的执行动作,但,在业务的持续变化中,由于某些接口约束已不再有用,我就需要不停的去修改这个接口模板,修改了模板之后,我还要去修改 V 层实现的方法,P 层实现的方法,并且,当业务量上来之后,整个接口调用层非常乱,为了跟踪一个功能,V 层跳 P ,P 层跳 V,跳了好几层,终于明白了这个功能实现了什么动作。

想法

我一直认为 ViewModel+LiveData+Lifecycles 是一个非常好的解决的方案,ViewModel 在 Framework 层面已经做了 ViewModelStore 的支持,Lifecycles 也在 ComponentActivity 中注册了 LifecycleRegistry,LiveData 在 observe(this,observer) 时持有了 LifecycleOwner,拥有了生命周期的感知能力。

1、第一点: 数据交互

View 层只需要初始化 ViewModel,订阅 ViewModel 中的 LiveData,以观察者的身份观察数据,一旦有数据的变化,就会自动更新到 View 中,而不需要像 V 与 P 的关系,在获取数据时,先触发 P,拿到数据后,P 层再调用 V 来更新数据,相比 ViewModel 的方式,多了一层手动设置数据返回,而为了数据返回,又多了一层接口模板来规范约束,最终,整个代码就是一个饼。

2、第二点:生命周期感知

LiveData 拥有的生命周期感知能力与 Present 还是有点区别的, 具体区别是在实现层面上:
1、LiveData 的生命周期感知是在 observe 时,将自己 addObserver 到 Lifecycle 中,让 Fragmentwork 层来赋予自己生命周期的感知能力
2、Present 生命周期感知是  BaseMVPActivity 赋予 BaseMVPPresent 的,如果我们的 Present 具有感知能力,就需要继承 BaseMVPPresent,这一点我在上面的继承中就表明了,这个是极差体验点,我们可以看下 ViewModel+LiveData 的方式,所有的操作都是在组合,组合的好处是什么呢?当然是为了在业务升级时有利于拆解和组装,而继承,不具有这样的特性

3、第三点:页面重建

屏幕旋转时页面发生重建,我们需要在在页面销毁时保存数据,重建时恢复,但数据的存储是有容量限制的,并且对象存储必须是序列化对象,苛刻的条件,Present 是无法做到的,然而,ViewModel 却是可以的,ViewModel 并不是通过 Bundle 的方式来实现数据的存储,而是通过 Framework 的支持,存储在 ViewModelStore 中,具体剖析可以查看文章 《ViewModel 凭什么能保存重建数据

其他想法的话,待补充吧!!!