MVX、Clean总结

MVC/MVP/MVVM/MVI

MVC

什么是 MVC?

MVC 其实是 Android 默认的设计。

  1. M: Model 数据类(数据的获取、存储、数据状态变化)
  2. V: View Layout XML 文件
  3. C: Controller Activity 负责处理数据、业务和 UI

MVC 流程

jg80n

MVC 缺点

Activity 责任不明、十分臃肿
Activity 由于其生命周期的功能,除了担任 View 层的部分职责(加载应用的布局、接受用户操作),还要承担 Controller 层的职责(业务逻辑的处理)
随着界面的增多 & 逻辑复杂度提高,Activity 类的代码量不断增加,越加臃肿

MVP

什么是 MVP?

MVP 是 MVC 架构的一个演化版,解决 MVC 中 Activity 作为 V 和 C 的问题:将 Activity/Fragment 中的业务逻辑分离出来,Activity/Fragment 只做 View 展示用,业务逻辑移动到 Presenter 层。

  1. M Model:实体类(数据的获取、存储、数据状态变化)
  2. V View:Activity/Fragment/Layout XML 文件
  3. P Presenter:负责 View 和 Model 之间的交互和业务逻辑

一般地,V 和 P 中间会定义一个契约层 Contract,规定 V 如何向 P 发指令和 P 如何 Callback 给 V 层。

MVP 流程

ugd4v

MVP 优缺点

优点

  1. 负责的逻辑处理放在 Presenter 进行处理,减少了 Activity 的臃肿
  2. M 和 V 解耦,Model 和 View 层完全分离,修改 View 层不会影响 Model 层
  3. 可以将一个 Presenter 用于多个视图,而不需要改变 Presenter 的逻辑
  4. Presenter 层和 View 层的交互是通过接口来进行的,便于单元测试

缺点

  1. 双向依赖:V 和 P 是双向依赖的,一旦 View 层做出改变,相应的 P 也需要做出调整,V 层变化是大概率事件
  2. 内存泄漏风险:P 持有了 V 层的引用,当用户关闭 V 层,P 层如果在子线程进行耗时操作,存在内存泄漏的风险,可以利用 Lifecycle 解决
  3. 协议接口类膨胀:V 和 P 交互需要定义接口,当交互复杂时,需要定义很多接口方法和回调方法,不好维护

MVVM

是什么 MVVM?

MVVM 与 MVP 的结构很相似的,就是将 Presenter 升级为 ViewModel。

  1. View:Activity/Fragment 和 Layout XML 文件,与 MVP 中 View 的概念相同;
  2. Model:负责管理业务数据逻辑(如网络请求、数据库处理),与 MVP 中的 Model 概念相同
  3. ViewModel:存储视图状态,负责处理表现逻辑,并将数据设置给可观察数据容器

实现细节上,V 和 P 从双向依赖变成 V 可以向 VM 发送指令,但 VM 不会直接向 V 回调,而是让 V 通过 _ 观察者的模式 _ 去监听 VM 层数据的变化,有效的规避了 MVP 中 V 和 P 双向依赖的问题。

qb9uf

DataBinding、ViewModel 和 LiveData 等组件是 Google 为了帮助我们实现 MVVM 模式提供的架构组件,它们并不是 MVVM 的本质,只是实现上的工具。

4era2

MVVM 存在的缺点

  1. 多数据流:View 和 VewModel 的交互分散,缺少唯一的修改源,不易于追踪
  2. LiveData 膨胀:复杂的页面需要定义多个 MutablLiveData,并且都需要暴露为不可变的 LiveData。
class TestViewModel : ViewModel() {
    // 为保证对外暴露的LiveData不可变,增加一个状态就要添加两个LiveData变量
    private val _pageState: MutableLiveData<PageState> = MutableLiveData()
    val pageState: LiveData<PageState> = _pageState
    private val _state1: MutableLiveData<String> = MutableLiveData()
    val state1: LiveData<String> = _state1
    private val _state2: MutableLiveData<String> = MutableLiveData()
    val state2: LiveData<String> = _state2
    //...
}
  1. 不用 DataBinding 的理由 我不使用Android DataBinding的四个原因
    1. JakeWharton 不推荐使用
    2. xml 里面写业务逻辑,难以维护
    3. 只适合用于简单的 app,

MVI

什么是 MVI?

MVI 和 MVVM 相似,借鉴了前框框架的思想,更加强调数据的单向流动和唯一数据源。

  1. Model:与 MVVM 的 Model 不同的是,MVI 的 Model 主要是指 UI 状态 State,并暴露出 ViewState 供 ViewState 订阅,ViewState 一般是个 data class 包含所有页面状态。(如页面加载状态、控件位置等)
  2. View:与其他 MVX 的 ViewState 一样,通常为 Activity 或 View;View 通过订阅 Model 变化实现界面刷新
  3. Intent:或者叫 Action,指用户的操作包装成 Intent 发送给 Model 层进行数据请求

MVI 交互流程

b4d0r
MVI 主要分为以下几步:

  1. 用户操作以 Intent(Action) 的形式通知 Model
  2. Model 基于 Intent 更新 State
  3. View 接收到 State 变化刷新 UI

在实现细节上,View 和 ViewModel 之间的多个交互(多 LiveData 数据流)变成了单数据流。无论 View 有多少个视图状态,只需要订阅一个 ViewState 便可以获取所有状态

MVI 优缺点

优点:

  1. 强调数据单向流动,很容易对状态变化进行跟踪和回溯
  2. State 是单一信息源(全局状态管理),不用担心各个 View 的状态到处都是;使用 ViewState 对 State 集中管理,只需要订阅一个 ViewState 便可获取页面的所有状态(MVVM 是多个 State)

缺点:

  1. **State 膨胀 **所有视图变化都转换为 ViewState(可以划分成几个小的分状态来缓解)
  2. **局部刷新 **View 根据 ViewState 响应,不易实现局部 Diff 刷新(自定义 LiveData 响应局部属性;Flow.distinctUntilChanged() 来减少不必要的刷新)
  3. **Event 的黏性事件问题:**LiveData 和 StateFlow 都有黏性问题

MVVM 和 MVI 的对比

1lg0l

eyodo

Clean

vapsl

分层

pn56x

分三层,每层都有自己的数据模型,每层之间通过接口方式来通信

Presentation Layer

也就是 MVX 结构所对应的地方(MVC、MVP 等),这里不处理 UI 以外的任何逻辑。

Domain Layer

业务逻辑,use case 实现的地方,在这里包含了 use case(用例) 以及 Business Objects(业务对象),按照洋葱图的依赖规则,这层属于最内层,也就是完全不依赖于外层。

Data Layer

所有 APP 需要的数据

Viper?

货拉拉提到