对于Android MVP的一些思考(二)

上次对MVP的表面概念作出了一些思考【对于MVP、响应式编程以及事件总线的一些思考】。而随着对自己的MVP框架深入优化、扩展,也发现了一些疑惑的地方。

Interface hell?

相信接触过MVP框架的都清楚,针对Model-View-Presenter这三者,都会为其单独建立一个interface,规范其各自的行为。举个例子,一个登录用的Activity,在这里将其命名为LoginActivity,其对应的Presenter和Model为LoginPresenterLoginModel,而其各自的接口则为ILoginViewILoginPresenterILoginModel
看,一个登录页面,就产生了3个接口文件。而随着应用复杂度提高,接口文件的数量将随之剧增——我将其称为“接口膨胀”。
因此,后来有人通过建立Contract类来规范接口,比如:

public interface LoginContract{
    public interface ILoginView{
        xxx...
    }

    public interface ILoginPresenter{
        xxx...
    }

    public interface ILoginModel{
        xxx...
    }
}

这样,一个页面对应一个Contract,减少了接口的生成数量。我觉得相对之前的情况,Contract的引入使得MVP项目相对简洁不少。
但是,我们是否真的需要这么多接口?

  • Model

    对此,google给出的mvp demo直接把Model的接口从Contract中去掉,Model接口视情况单独建立,Model由Presenter决定如何调用、调用的数量。

    我认为Model确实可以把接口完全去掉(视情况),因为Presnter仅仅是调用Model,将Model的数据和View结合,可以预见的是随着应用功能的增加,一个P将可能调用多个M来获取数据。总之,Model的接口在此处重要性并不高。

  • View

    我查看了github上一些mvp的开源项目,虽然各有不同之处,但View的接口应该是大多数人都认为不能省却的。一是View层接口是为了规范View的活动,相对P层而言,能够更直观地关注V的方法;二是提供View接口方便View的解耦。应用升级除了内在功能点改进、应用优化、错误修复之外,变动最多的就是View,接口的解耦方便项目可以方便地更改甚至替换View。

  • Presenter

    Presenter的接口去留,是目前争议最大的地方。一些文章给出了很有趣的观点:

    第一篇文章中的作者认为,虽然他认同P的interface并不是一定需要的,但是他仍然会将P的interface写进Contract中,一是认为这是定义V和P之间的合作契约,二是这实际上并不太花费功夫。
    第二篇文章中的作者观点很明确(看标题就知道),就是认为P的interface完全不需要,写来也是浪费时间。在这篇文章下的评论也有持反对意见的,但还是赞同的比较多。

对此,我觉得应该再次审视MVP的本质——就是解耦。而接口是最充分发挥解耦功能的方式。而如何和应用的实际情况相结合,这是一个动态平衡的问题。也许有的项目针对Model层的改动很大,调用的地方很多,这时候Model完全抛弃接口不一定是件好事;而有的项目P层的使用极为有限,建立过多的interface确实是浪费时间和增加Method数。
所以对我而言,我的取舍是:Model去掉接口,但是每个V针对一个M,即确保V:P:M是1:1:1的关系。这样即使要改动一个M,也仅是改动一个P即可。虽然这样也是有缺点:当M层内所调用的多个操作有所变动,可能需要改动特别多的地方,但这保证了只影响M层,其他层不受影响。
而P层的Interface,我还是选择保留,与View共同写在Contract中——除非对Method数有要求。

如何建包?

其实这是一个很小的点。比如说,有的人喜欢分类将所有Activity放到同一个包中,按类型建包;有的喜欢按照需求功能,比如Login部分都放到同一个包,按功能建包。
按照google的demo,结合Contract,demo选的是按功能建包,同一功能的都放到同一个包中。
但是会有这样一种情况——假设我现在写一个应用内的文章模块,它包含有:

  1. 列表页
  2. 详情页
  3. 评论详情页
  4. 投稿页
  5. etc.

这样,如果按照上述分包策略,这部分的所有M、V、P都放进一个article的包内,是否又显得过于臃肿?
又或者,再按照功能点细分,就会有如下分包

  • article
    • articlelist
    • articledetail
    • commentdetail
    • postarticle

这样,保证了每个包内仅有一组 M、V、P和Contract,结构上是非常清晰。不过,我们是否真的需要如此多的包呢?
其实只要不是按照上述第一种方法分包,恐怕任何方式的分包都会导致包的数量增加。我看到有的项目会将Contract抽离出单独一个包,所有Contract都写在里面;而M、V、P都各自一个包,虽然这样也够直观,但是各自内部不会再分包,这样又是否够清晰呢?

事实上,分包也只是个人喜好,到底如何分,随君喜好吧!

数据缓存

有一种情况:当应用回到后台,再次回到前台时,可能V层已经被内存回收,这时候如何让P层与V层重新建立联系,并且尽可能恢复数据?

上述文章里有提到这种情况,但事实上很多讲述MVP架构的文章都一再强调:

  1. P层与V层共存亡
  2. 数据不应保存在P层

是的,一个应用所产生的临时数据不应该记录在P层,但一些临时变量可以。比如一个列表页的数据,从服务器获取下来后,应该在M层保持住;而用户滑动到列表页的第几层,则可以记录在P层。

那么,如何恢复数据呢?
首先明确一个观点,即上述第一点,P与V共存亡,既然V被回收了,P也不会存在,所以V重新被创建了,P也随着V的创建而创建。
所以第一种方法,就是最常用的,将数据保存到V的Bundle中,随着V被重新创建,取出Bundle的数据给回P层,再从P层回到M层。

但是上述文章有提到一点,即把数据保持在M层。
M层其实并不是必须跟随V的生命周期,毕竟它不关心P、V,它只负责操作数据。所以M层可以通过静态变量等手段保持数据到内存,当P、V重新建立时,再从M处取得数据。
实际上这种方式更符合MVP的思想,甚至由此可以辅助处理不常变动的网络数据,将数据缓存到内存中,无需多次请求网络,毕竟相比之下请求网络的操作和内存相比代价还是略高。

在这里,我只给出这种思考点,至于如何使用,或者有更好的方式,都要看实际需求决定。

数据传递

依然举一个例子:当一个Activity内用ViewPager之类的包裹4个Fragment,如何合理地处理其逻辑和数据交互?
起初我的想法是,Fragment也只是一个控件View,因此我认为”Fragment和Activity共用一个Presenter和Model“,这样就能减少管理复杂度,也无需考虑数据的传递——毕竟数据都在同一个Presenter和Model里打转。

后来我意识到这个想法是错误的,一是这样违背了使用MVP的初衷,二是Fragment也有其自身的生命周期,以及它自身的特性注定了它可以承担复杂的页面逻辑,这些在MVP架构下离不开P的加持。

回到上述问题,我认为数据的交互至少要利用Model来处理。结合上一篇的思考内容,应当在M层建立(或者调用)Repository,提供订阅功能。例如,根据上面问题的场景,可以得出3种数据流向:

  1. 创建Fragment时通过setArgument()设置参数:Activity ---> Fragment(create)
  2. Activity和Fragment之间互相交互:Activity <===> Fragment
  3. Fragment之间互相交互:Fragment <===> Fragment

排除第一种流向,最麻烦的就是在第2、3种情况下,彼此交互的数据类型十分多(比如Activity和Fragment、Fragment之间的数据交互类型实例分别有5~10种),怎么办呢?

我认为的解决方法是,在不使用Bus类的方法上,针对每种数据类型,都建立对应的Repository,在需要交互的V层所对应的Model上调用其对应的Repository,并根据Repository提供订阅的Observable,供P层调用,毕竟决定交互数据的逻辑是由P决定的;然后,当其中一个V产生数据,将通过P层传递到M层,M层内部将数据发送到Repository,而订阅了该Repository的P层将收到数据从而处理V层。

是的,上述的方法稍微有点傻,但是考虑到解耦和日后修改时的难易度,个人感觉可以接受。

什么?你说如果数据类型在10种、20种、甚至上百种呢?
那就重构吧!

总结

架构往往是提供一种思考范式,而不是硬性规定,任何架构或者思想总有无法涉及或者无法处理的问题,这时候就考验到使用者如何灵活地变通运用。MVP是个很好的框架,但基于Android自身的设计,始终无法做到彻底地将各层次分离开来;而引入响应式编程,我觉得是一个很好的灵活处理方式,虽然MVP不是必须依赖响应式,但是通过事件响应、数据响应,可以更好地处理MVP里同层级间的联系(如M与M间的数据流动),这一点是很多文章没有提及到的。

这些思考仅一己之见,还需实战实验,还望能抛砖引玉!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值