大数跨境
0
0

MVP“变形记”,苏宁移动开发的架构演进!

MVP“变形记”,苏宁移动开发的架构演进! 苏宁科技
2017-12-01
2
导读:App 客户端如何在外部需求不断变化的情况下,降低模块耦合,尽可能减少每次代码修改量?

苏宁+App 是苏宁易购集团零售云研发中心主要产品之一,由于项目处于初期阶段,业务逻辑复杂,导致业务需求变动快,常常在开发甚至测试过程中出现界面或者后台接口调整的情况。


App 客户端如何在外部需求不断变化的情况下,降低模块耦合,尽可能减少每次代码修改量,一方面减少开发人员的工作量,另一方面降低测试工程师的工作量,最终顺利完成项目迭代开发。

 


为什么使用 MVP 模式



相信在 2014 年之前,绝大部分人开发 Android 应用,都是使用的 MVC 模式。


M 跟 V 一般没有什么问题,Controller 层也就是对应 Activity 类,它的首要职责是加载应用的布局和初始化用户界面,并接受和处理来自用户的操作请求,进而作出响应。


随着界面及其逻辑的复杂度不断提升,Activity 类的职责不断增加,以致变得庞大臃肿,打开以前项目的 Activity,超过 2000 行的不在少数。


另外,由于项目的特殊性,互联网产品讲究速度,尤其是新产品,上线的时间会决定你在市场上的占有量。


App 的理想情况是 UED 做好视觉稿,后台接口准备完毕,客户端同学一边做页面一边调试接口,做完自测后顺利交付测试。但是,理想很丰满,现实很骨感。


实际情况是,我们开始 Coding 的时候,只有一份接口文档跟交互图。我们需要思考的是,我们必须把界面和接口数据解耦,接口联调和测试工作不能依赖界面的完成,当完成业务层代码后,就可以测试业务功能。


基于上面的背景,我们选择了 MVP 模式。



什么是 MVP 模式



我们上面说的 MVP 架构,是 Google 开源的一个设计模式,它主要是为了细分视图(View)与模型(Model)的功能,让 View 只做两件事:

  • 完成用户的交互。

  • 显示界面布局,同时让 Model 做数据的处理,业务逻辑放到另外的一个类(Presenter)中。


下面做具体分析:

  • M 即 M 层,在项目中负责数据的处理,包括本地数据库查询,网络数据获取都在这一层中完成。

  • View 即 V 层,在项目中是 UI 模块,也就是各种 activity/fragment,负责绘制 UI 元素、与用户进行交互。

  • 即 P 层,在项目中做为 View 与 Model 的桥梁,M 跟 V 层不直接交互,M 层在获取到数据之后,传递到 P,P 层再通过接口回调到 View 层,同样,View 层的点击等事件,通过 P 层去通知 M 层去处理。


如下图所示:


MVP 模式应用实战




苏宁+App 项目结构



苏宁+App 项目结构图如下:

目前 App 整体项目架构如图中所示,各个层次的介绍如下:

  • 前端界面层:界面相关布局,如各种 activity/fragment 类。

  • 业务逻辑层:业务逻辑相关,如各种 Presenter 类。

  • 数据层:数据相关,包括数据存储,获取,如各种 Model 类。

  • 运行服务层:伴随应用生命周期自动初始化,自动销毁,提供一系列服务给其他业务模块调用,如各种 Service 类。

  • 业务框架层:针对当前 App 跟业务有耦合度的公共方法,组件抽取。

  • 基础框架层:跟业务无关的底层组件,可以给多个 App 同时使用。

  • 系统层:Android 系统底层。


通过上面的架构图可以很直观的看出,我们日常业务功能迭代的时候,主要修改或者新增的代码都在前面三层,这里主要讲前面三层的使用规范。



目录结构



下图为使用 MVP 模式时,购物车确认订单页面的目录结构:

  • model—数据处理。

  • presenter—业务处理。

  • task—网络请求。

  • ui—页面。

  • util—当前模块公共类。

  • view—页面刷新回调接口。



总体逻辑设计



如下图,为购物车 2 界面,下面将围绕该界面来讲解如何用 MVP 实现具体业务功能。

为了更加直观看到 MVP 在当前业务中的使用,我们画了类图跟时序图,通过类图我们可以清楚类的设计,如下所示:

通过下面的时序图,我们可以很清楚的看到调用关系:

通过上面两张图,我们可以看到 MVP 在当前业务中对应的角色以及调用关系,下面深入代码层面继续讲解。



代码实现



M 层(model)


项目中很多网络请求是重复的,比如很多页面都会用到店铺信息接口,如果每个页面都要在不同 Model 写一遍,那么复用性很弱。


所以跟 Google 在 Github 发布的 MVPDemo 不同,我们项目中每个网络接口都单独写成一个 Task,以确认订单页面为例:

  • Model 层定义模型抽象类(PSCShopCart2DataSource)。

  • 然后具体实现类(PSCShopCart2Repository)里面调用 Task,发送网络请求。


代码如下:

IView 


MVP 模式中,M 层跟 V 层不能直接通信,数据是通过 Presenter 层接口回调到 V 层中。一般情况下,IView 里面的接口就对应 V 层的功能。


这边会有人觉得特别复杂的场景会出现很多接口的情况,当然如果真出现这种情况,该合并的接口还是要合并,到 Activity 中做简单的处理也是可以的。


实际开发中一定不能被框架限制,不管什么模式都是为了业务正常迭代。


代码如下:

P 层(presenter)


原先杂糅在 activity/fragment 里面的业务逻辑移到 Presenter中,同时 Presenter 做为 M 和 V 之间交互的桥梁。


由于 Activity 跟 Fragment 生命周期不同,会影响一些弹出框关闭的时机,所以项目中,Activity 跟 Fragment 分别定义了一套基础业务抽象类。


这边以 Activity 基础业务抽象类来演示,所有的 Activity 中用到的 Presenter 都继承 PSCBaseActivityPresenter:

PSCActivityNetTask 主要做网络任务监听并回调到 Presenter 中,还会设置生命周期监听,用于显示加载框。

Presenter 接受到网络回调后,根据接口返回的数据做业务处理,成功或者失败分别通过接口回调到 View 层,刷新界面。

V 层(view)


相信大多数 App 都会有 baseActivity 作为基类,将 Activity 公共部分抽取出来进行封装。


苏宁的基类叫做 SuningActivity/SuningFragment,每个界面都需要把 View 跟 Presenter 绑定/解绑,这些都可以放到基类中。


然后定义 protected abstract TcreatePresenter();将创建 Presenter 步骤交给子类实现。


代码量比较大,这边做了删减,仅保留 MVP 相关的代码,如下:

Activity 实现上面定义的 IView,实现数据的接收,同时会在当前类中创建 Presenter,通过 Presenter 方法调用 Model 中的网络请求。


总结



以上内容就是我们对于 MVP 架构的理解,并在苏宁+项目中实战后分享给大家。


MVC、MVPMVVM 不管何种模式,都可以实现功能,选择相应模式的时候,要看相对于目前业务来说的,何种模式能够封装变化,让各模块解耦,实现独立变化,减少日后的维护工作和暗藏的风险。


当然我们也不能陷入模式的陷阱,为了使用模式而去套模式。没有好的框架,只有适合的框架,如果大家发现我们当前项目中对于 MVP 的使用不对或者不完善的地方,欢迎提出来,我们一起探讨。


【声明】内容源于网络
0
0
苏宁科技
苏宁技术官方号。解读前沿零售技术,分享苏宁技术实践。
内容 623
粉丝 0
苏宁科技 苏宁技术官方号。解读前沿零售技术,分享苏宁技术实践。
总阅读9
粉丝0
内容623