|
|
|
|
移动端

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

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

作者:曹银飞来源:51CTO技术栈|2017-11-29 09:34

【新品产上线啦】51CTO播客,随时随地,碎片化学习

【51CTO.com原创稿件】苏宁+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 即 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、MVP、MVVM 不管何种模式,都可以实现功能,选择相应模式的时候,要看相对于目前业务来说的,何种模式能够封装变化,让各模块解耦,实现独立变化,减少日后的维护工作和暗藏的风险。

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

曹银飞,苏宁云商 IT 总部 Android 技术专家,拥有多年 Android 研发和管理经验。曾就职于联创、腾讯等大型互联网公司,现负责苏宁易购 Android 开发部产品研发与技术管理工作,在 Android 项目架构设计,性能优化,团队管理上有多年的实战经验。现致力于打造苏宁智慧零售相关 App,希望将苏宁的零售技术能力发挥到极致。

【51CTO原创稿件,合作站点转载请注明原文作者和出处为51CTO.com】

【编辑推荐】

  1. 总结个人使用过的移动端布局方法
  2. 开发者必读 移动端页面优化的10个好方法
  3. 移动动态化方案在蜂鸟的架构演进(含React Native与Weex对比)
  4. 5亿会员融合技术实践,力促苏宁818井喷增长!
  5. 挑战“零事故”,苏宁易购O2O购物节大促保障之道!
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

Linux指令速查手册

Linux是一款开源的操作系统,得到了广大开发者的青睐。掌握Linux系统的指令及其用法是学习Linux系统的基础。本书详细地介绍了常用Linux指令...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊