文章

MVX、Clean总结

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

  • View 接收用户的交互请求,View 将请求转交给 Controller
  • Controller 操作 Model 进行数据更新,数据更新之后,Model 通知 View 数据变化
  • View 显示更新之后的数据

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

  • View 接收用户交互请求,View 将请求转交给 Presenter(V 调用 P 接口)
  • Presenter 操作 Model 进行数据更新(P 调用 M 接口)
  • Model 通知 Presenter 数据发生变化(M 调用 P 接口)
  • Presenter 更新 View 数据(P 执行 View 的接口方法,也就调用了 V 的实现类)

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。
1
2
3
4
5
6
7
8
9
10
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 的对比

  • 对于新的团队,一般都熟悉 MVVM,MVVM 架构的接受度肯定会好
  • 对于复杂的场景,采用 MVI 的全局状态管理思路更优
  • MVVM 和 MVI 的复杂度对比
    • MVVM
      • View 发起请求的复杂度:ViewModel 的各种方法调用会散落在界面不同地方(即界面向 ViewModel 发起请求没有统一入口)
      • View 观察数据的复杂度:界面需要观察多个 ViewModel 提供的数据,导致界面状态的一致性难以维护
      • ViewModel 内部请求和数据关系的复杂度:数据被定义为 ViewModel 的成员变量。成员变量是增加复杂度的利器,因为它可以被任何成员方法访问。也就是说,新增业务对成员变量的修改可能影响老业务的界面展示。同理,当界面展示出错时,也很难一下子定位到是哪个请求造成的。

1lg0l

  • MVI:解决了上面的三个问题
    • State 单一数据源
    • Event 需要消除黏性,SharedFlow/SingleLiveEvent

eyodo

Clean

vapsl

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

Presentation Layer

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

Domain Layer

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

Data Layer

所有 APP 需要的数据

Viper?

货拉拉提到

本文由作者按照 CC BY 4.0 进行授权