MVI学习
文章
[[Flow学习]]
响应式编程
MVI 是在响应式编程的前提下,通过 “将页面状态聚合” 来统一消除上述 2 个问题,
也即原先分散在各个 LiveData 中的 String、Boolean 等状态,现全部聚合到一个 JavaBean / data class 中,由唯一的粘性观察者回推,所有控件都在该观察者中响应数据的变化。
MVI 架构为了解决 MVVM 在逻辑复杂时需要写多个 LiveData (可变+不可变) 的问题,使用 ViewState 对 State 集中管理,只需要订阅一个 ViewState 便可获取页面的所有状态
通过集中管理 ViewState,只需对外暴露一个 LiveData,解决了 MVVM 模式下 LiveData 膨胀的问题
1 |
|
但页面的所有状态都通过一个 LiveData 来管理,也带来了一个严重的问题,即页面不支持局部刷新
虽说如果是 RecyclerView 可以通过 DifferUtil 来解决,但毕竟不是所有页面都是通过 RecyclerView 写的,支持 DifferUtil 也有一定的开发成本
因此直接使用 MVI 架构会带来一定的性能损耗,相信这是很多人不愿意用 MVI 架构的原因之一
MVI
由于没有明确的状态管理标准,随着应用程序的增长或添加功能或事先没有计划的功能,视图渲染和业务逻辑可能会变得有点混乱,并且这种情况经常发生在 Android 应用开发过程中。可能你经常遇到状态管理导致业务逻辑和 UI 渲染的分工不明确,最终导致应用架构的混乱。而新提出的 MVI 架构,提倡一种单向数据流的设计思想,非常适合数据驱动型的 UI 展示项目。MVI 的架构思想来源于前端,由于 Model、View 和 Intent 三部分组成。
Model: 与其他 MVVM 中的 Model 不同的是,MVI 的 Model 主要指 UI 状态(State)。当前界面展示的内容无非就是 UI 状态的一个快照:例如数据加载过程、控件位置等都是一种 UI 状态
View: 与 MVvM 中的 View 一致,可能是一个 Activity、Fragment 或者任意 UI 承载单元。MVI 中的 View 通过订阅 Intent 的变化实现界面刷新
Intent: 此 Intent 不是 Activity 的 Intent,用户的任何操作都被包装成 Intent 后发送给 Model 进行数据请求
运用函数式编程思想将需求翻译成业务意图(I)、数据(M)、界面状态(V)间的函数关系,再用响应式编程的方式将其串联成数据流的过程。
个人感觉 mvi 和 mvvm 的区别就是 mvi 把 viewmodel 的方法改成了一个来统一处理,通过它的参数 intent 来区分不同调用。v 层也是一样,原来每个业务都有自己的 livedata,v 层需要对每一个 livedata 单独监听。如果使用 mvi 只需要使用一个 livedata 就可以了,利用 state 来区分不同的业务。
单向数据流
==MVI 强调数据的单向流动,主要分为以下几步==
==Intent 和 action 类似==
- 用户操作以
Intent
的形式通知Model
Model
基于Intent
更新State
View
接收到State
变化刷新 UI。
数据永远在一个环形结构中单向流动,不能反向流动
我们使用 ViewModel
来承载 MVI
的 Model
层,总体结构也与 MVVM
类似, 主要区别在于 Model
与 View
层交互的部分
Model
层承载UI
状态,并暴露出ViewState
供View
订阅,ViewState
是个data class
,包含所有页面状态View
层通过Action
更新ViewState
,替代MVVM
通过调用ViewModel
方法交互的方式。(Action 可以理解成 intent)
实例
- 大型页面使用,很多 liveData, 一俩个 liveData 不需要 MVi
- 用户的任何操作都被包装成 Intent 发送给 Model 进行数据请求
大方向流程:
- view 点击调 viewModle 方法,把 intent(意图)发过去
- 通过 intent 更新 state(Model)
- view 接收到
State
变化刷新 UI。
Action 这一层可以加也可以不加, Mavericks
中 View
层与 Model
层的交互,也并没有包装成 Action
,而是直接暴露的方法
上篇文章也的确有很多同学说使用 Action
交互比较麻烦,看起来 Action
这层的确可要可不要
尘埃落地 😛 遍历全网Android-MVI架构,学习总结一波 - 掘金
1 |
|
View
通过 Action
与 ViewModel
交互,通过 Action
通信,有利于 View
与 ViewModel
之间的进一步解耦,同时所有调用以 Action
的形式汇总到一处,也有利于对行为的集中分析和监控
缺点
MVI
也有一些缺点,比如
- 所有的操作最终都会转换成
State
,所以当复杂页面的State
容易膨胀 state
是不变的,因此每当state
需要更新时都要创建新对象替代老对象,这会带来一定内存开销
软件开发中没有银弹,所有架构都不是完美的,有自己的适用场景, 读者可根据自己的需求选择使用。
- 使用
ViewState
对State
集中管理,只需要订阅一个ViewState
便可获取页面的所有状态 - 通过集中管理
ViewState
,只需对外暴露一个LiveData
,解决了MVVM
模式下LiveData
膨胀的问题
但页面的所有状态都通过一个 LiveData
来管理,也带来了一个严重的问题,即页面不支持局部刷新
虽说如果是 RecyclerView
可以通过 DifferUtil
来解决,但毕竟不是所有页面都是通过 RecyclerView
写的,支持 DifferUtil
也有一定的开发成本
因此直接使用 MVI
架构会带来一定的性能损耗,相信这是很多人不愿意用 MVI
架构的原因之一
规范
1 |
|
这段代码是在使用 MVI(Model-View-Intent)架构模式时常见的一种写法。
首先,_viewStates
是一个私有的 MutableLiveData
对象,用于保存界面的状态。MutableLiveData
是 Android Jetpack 中的一个组件,用于在组件之间进行数据通信。
接着,viewStates
是一个公开的只读属性,它通过调用 _viewStates
的 asLiveData()
方法返回一个不可变的 LiveData
对象。LiveData
是一个被观察的数据持有者,可以通知观察者有关数据的变化。
在 MVI 架构中,viewStates
属性通常用于将界面的状态暴露给视图层(View Layer)。通过观察 viewStates
属性,视图层可以及时地获取最新的界面状态,并相应地更新用户界面。
为什么要使用这样的写法呢?MVI 架构的一个关键思想是单向数据流,即将界面的状态作为单一的源头,通过派发意图(Intents)和处理器(Reducers)来改变状态。通过将 _viewStates
声明为私有的 MutableLiveData
,可以确保只有该类内部才能修改界面状态,而外部只能观察状态的变化。
此外,将 _viewStates
转换为不可变的 LiveData
对象可以保护数据的封装性,防止外部组件直接修改内部状态。
总结起来,这段代码的作用是在 MVI 架构中定义一个用于保存界面状态的私有 MutableLiveData
对象 _viewStates
,并通过公开的只读属性 viewStates
将其暴露给视图层。这样可以确保界面状态的封装性和单向数据流的规范性。
密封类 sealed
Kotlin 中的 sealed 类是一种特殊类型的类,用于限制继承关系。当一个类被声明为 sealed 类时,它只能被其所在文件中的类继承,这意味着你无法在其他文件中继承它。
sealed 类常用于建模一组有限的类型,比如表示某个状态或者某个特定类型的集合。通过将这些相关的类型定义为 sealed 类的子类,可以在编译期间对所有可能的类型进行静态检查。
sealed 类与普通类的区别在于,必须将其所有可能的子类定义在同一文件中,且这些子类必须是 sealed 类的直接子类。这样做的好处是,在编译器能够检查出所有可能的子类,并且可以在 when 表达式中使用密封类作为分支条件,而不需要添加 else 分支。
1 |
|
在上述示例中,我们定义了一个 sealed 类 Result,并将它的两个可能的子类 Success 和 Error 定义在同一文件中。然后我们编写了一个函数 processResult,根据传入的 Result 对象进行处理。通过使用 when 表达式,我们能够对 Result 类型的不同子类进行分支处理。
总而言之,Kotlin 中的 sealed 类提供了一种方便的方式来建模有限类型集合,并在编译期间进行类型检查,使代码更加安全和可靠。
例子
下面是 chatGPT 给的例子
1. 创建一个 Model 类:
1 |
|
2. 创建一个 Intent 类:
1 |
|
3. 创建一个 ViewState 类:
1 |
|
4. 创建一个 ViewModel 类:
1 |
|
5. 在 Activity 或 Fragment 中使用 ViewModel:
1 |
|
在这个示例中,我们使用了 ViewModel 来处理业务逻辑和状态管理,同时使用 LiveData 来观察状态的变化。通过定义 Model、Intent、ViewState 来区分数据模型、用户操作意图、界面状态,实现了简单的 MVI 架构。希望这个例子能帮助你更好地理解如何在 Android 应用中使用 MVI 架构。