Android MVP 架构学习(一)

最近做练手项目的时候,明显感觉项目的组织结构特别混乱,尤其 Activity 的代码飞速膨胀。之后,不得不暂停项目,搜索相关的解决办法,一步步学习常用的 Android 架构模式,而目前最常见的则是 MVP。

简介

MVP 是 Model-View-Presenter 的简写。作为一种软件项目的架构模式,它的各个部分的责任分别是:

  • Model:
    • 获取和管理数据
    • 提供细节无关的接口方便 Presenter 调用
  • View:
    • 界面绘制
    • 向 Presenter 传递信息
    • 接收 Presenter 命令改变界面
  • Presenter:
    • 从 Model 的接口获取数据
    • 提供接口传递数据给 View
    • 使用接口更新 Model 数据
    • 发出命令更新界面

背景

官方培训指南虽然说 Android 的项目默认实现 MVC 模式,但是实际上默认的项目结构很糟糕,所以才要采用其他的模式来组织和管理代码。 默认情况下,XML 文件和部分自定义 View 的 Java 代码组成 View 部分,Activity 以及 Fragment 是 Controller,模型类由用户定义,系统并且提供基础的辅助类(比如:SQLiteOpenHelper、HttpURLConnection 等)。 使用这样的组织方式的后果:

  • View 的代码非常简单,几乎不符合 MVC 中 View 的设计初衷
  • Model 的代码可以让程序员控制,还有很多的第三方库辅助,问题反倒不大
  • Controller 部分非常糟糕,确切的说是因为 Activity 的结构过于臃肿,让原本很好的设计初衷变形走样

因为,只要稍稍观察源代码就知道:

  • Activity 是 Context 的子类,Context 则是一个上帝类(God Class),Activity 自然成了上帝对象(God Object)
  • Activity 有一个复杂的生命周期,令 Controller 的代码变得非常复杂,极易导致内存泄漏
  • Activity 很难测试,尤其掺杂业务逻辑之后测试难度更高

详细的解释以上的原因为: Context 的功能太多了,再加上 Activity 生命周期相关的方法以及有关界面绘制的方法,原始的 Activity 的方法就已经很多了,何况作为控制器用户还要编写更多的控制逻辑方法放在其中。最后,Activity 的代码会急速膨胀,成为维护时的噩梦。 更糟糕的是,很多时候 Activity 中常常包括不少 View 内容的代码(比如:动态修改界面、生成 View 等),这些代码甚至杂糅了部分 Controller 的功能。另外,不讲究的程序员还可能直接在 Activity 中访问 Model 部分的底层代码,使得 Controller 又和 Model 紧紧粘合——毕竟,官方的项目结构和分层指导并没有禁止这样的行为。这样的项目,莫说是维护,即便只是阅读它都让人痛苦不已。 Activity 的生命周期的流动比较特别,像是旋转设备、分屏、内存回收等行为都会对它产生很大的影响。当它持有或者被持有引用的时候,代码就会不可避免的变得复杂,以判断、处理数据的缓存和恢复情况,小心地避免内存泄漏或者数据丢失地发生。而作为 Controller 的 Activity 不可避免的要设计这样的操作,风险极大。 Activity 本身通过回调方法委托系统管理生命周期,同时又会有其他的交流方法,方法极多且调用复杂,本身就是很难测试的类。如果 Activity 加入大量业务逻辑代码,同时还要应对生命周期变化产生的状态变化和恢复,测试难度会高得让人难以接受。

实际上,要改进原生 MVC 模式的失败实现,最主要的目标就是减弱 Activity 的职责,避免系统代码的负面影响。比较好的办法是把 Activity 整合到 View 或者 Model 层中,剥离它的控制逻辑代码组成新的一层结构,由程序员自己设计和编写。仔细观察可以明白:Activity 的代码中本身就含有很多 UI 相关的元素,而且原来旧 MVC 模式中 Model 层的代码旧相对自由和它没什么联系,所以结果就是将 Activity 归入 View 层。 最后的结果就是,Activity 成为新的 View 层组件,负责沟通新抽取的中间层,向它传递信息,并等候它的命令来更新 UI。而新的中间层则负责和 Model 层通信,以及实现业务逻辑,并且根据业务逻辑发布命令更新 Model 和 View。这样,实际就是 MVP 模式。

简单地说,二者的区别在于各部分的职责以及通信的方式不同:

mvc.png

  • MVC 模式的各个组件可以互相通信
  • 理想的 MVC 模式中 Controller 都比较简单,只是起到简单的中转的作用,甚至有些极端的项目会取消 Controller 层,而设计一个简单的 Router 来中转信息
  • View 层代码较多,负责处理大部分的业务

mvp.png

  • MVP 模式中 View 和 Model 之间不可通信,而是通过 Presenter 中转
  • Presenter 要程度大量的业务逻辑职责,相对很厚重,View 则极为轻薄

可以看出,如果要实现 MVC 模式,提取一个较轻薄的中间层作为 Controller 问题不大,但是 Activity 做 MVC 中的 View 则很不合适,它又会负担很多的业务逻辑代码飞速膨胀,实际没有解决任何问题。而且,相对来说,MVC 模式中因为各层之间互相连接,一旦发生变化,就要相应地更新 3 个层地相关组件,这需要成熟且复杂的框架来保证更新操作以及通信操作的正确和高效 —— 一来现在还没有广泛应用的 MVC 框架,二来重型框架对应移动端项目来说代价太高了。

后续文章

Android MVP 架构学习(二)

参考文章

MVC,MVP 和 MVVM 的图示

Model-View-Presenter: Android guidelines