MVI是什么?
MVI中 分为UI层、网域层、与数据层,我造个词叫他UDD,其中网域层可有可无,网域层我们最后再来看。我这里不会再一一截图来展示UI层怎么样、数据层怎么样,可直接看官网。(其实相比较于MVVM基本没变化)
MVI是最近被引入Android的。受Cycle.js框架的思路影响,是基于单向圆柱流的原理进行工作的。框架中主要以下面几个角色为主:
- Model层:这里的Model不是其他框架中的Model层,在MVI框架中表示存储UI的状态
- View层:在MVI中View层主要是接口,负责相应UI的状态
- Intent层:在MVI中Intent(和Android中的Intent不是同一个)主要负责传递UI状态
MVI数据流
当用户进行操作时,程序会以Intent的的形式通知Model层,Model层会对这个UI状态做处理在回调View层进行UI的状态刷新,数据永远都是一个环形结构且单向流动,请看下图:
MVI的优缺点
优点:
- UI的所有变化来自State,所以只需聚焦State,架构更简单、易于调试
- 数据单向流动,很容易对状态变化进行跟踪和回溯
- State实例都是不可变的,确保线程安全
- UI只是反应State的变化,没有额外逻辑,可以被轻松替换或复用
缺点:
- 所有的操作最终都会转换成State,所以当复杂页面的State容易膨胀
- State是不变的,每当State需要更新时都要创建新对象替代老对象,这会带来一定内存开销
MVI架构实战
总体架构图
我们使用ViewModel来承载MVI的Model层,总体结构也与MVVM类似,主要区别在于Model与View层交互的部分。
- Model层承载UI状态,并暴露出ViewState供View订阅,ViewState是个data class,包含所有页面状态。
- View层通过Action更新ViewState,替代MVVM通过调用ViewModel方法交互的方式。
MVI实例介绍
添加ViewState与ViewEvent
ViewState承载页面的所有状态,ViewEvent则是一次性事件,如Toast等,如下所示:
data class MainViewState(val fetchStatus: FetchStatus, val newsList: List<NewsItem>)
sealed class MainViewEvent {
data class ShowSnackbar(val message: String) : MainViewEvent()
data class ShowToast(val message: String) : MainViewEvent()
}
- 我们这里ViewState只定义了两个,一个是请求状态,一个是页面数据。
- ViewEvent也很简单,一个简单的密封类,显示Toast与Snackbar。
ViewState更新
class MainViewModel : ViewModel() {
private val _viewStates: MutableLiveData<MainViewState> = MutableLiveData()
val viewStates = _viewStates.asLiveData()
private val _viewEvents: SingleLiveEvent<MainViewEvent> = SingleLiveEvent()
val viewEvents = _viewEvents.asLiveData()
init {
emit(MainViewState(fetchStatus = FetchStatus.NotFetched, newsList = emptyList()))
}
private fun fabClicked() {
count++
emit(MainViewEvent.ShowToast(message = "Fab clicked count $count"))
}
private fun emit(state: MainViewState?) {
_viewStates.value = state
}
private fun emit(event: MainViewEvent?) {
_viewEvents.value = event
}
}
如上所示:
- 我们只需定义ViewState与ViewEvent两个State,后续增加状态时在data class中添加即可,不需要再写模板代码。
- ViewEvents是一次性的,通过SingleLiveEvent实现,当然你也可以用Channel当来实现。
- 当状态更新时,通过emit来更新状态。
View监听ViewState
private fun initViewModel() {
viewModel.viewStates.observe(this) {
renderViewState(it)
}
viewModel.viewEvents.observe(this) {
renderViewEvent(it)
}
}
如上所示,MVI 使用 ViewState 对 State 集中管理,只需要订阅一个 ViewState 便可获取页面的所有状态,相对 MVVM 减少了不少模板代码。
View通过Action更新State
class MainActivity : AppCompatActivity() {
private fun initView() {
fabStar.setOnClickListener {
viewModel.dispatch(MainViewAction.FabClicked)
}
}
}
class MainViewModel : ViewModel() {
fun dispatch(action: MainViewAction) =
reduce(viewStates.value, action)
private fun reduce(state: MainViewState?, viewAction: MainViewAction) {
when (viewAction) {
is MainViewAction.NewsItemClicked -> newsItemClicked(viewAction.newsItem)
MainViewAction.FabClicked -> fabClicked()
MainViewAction.OnSwipeRefresh -> fetchNews(state)
MainViewAction.FetchNews -> fetchNews(state)
}
}
}
如上所示,View通过Action与ViewModel交互,通过 Action 通信,有利于 View 与 ViewModel 之间的进一步解耦,同时所有调用以 Action 的形式汇总到一处,也有利于对行为的集中分析和监控。
Android中的框架有许多最常见的有 MVC、MVP、MVVM 等。其中MVVM更是被官方推荐,成为Android开发中的显学。 不过软件开发中没有银弹,MVVM架构也不是尽善尽美的,在使用过程中也会有一些不太方便之处,而MVI可以很好的解决一部分MVVM的痛点。 更多Android核心技术学习,大家可以前往这里领取一套《Android核心技术手册》进行参考辅导。
MVI总结
MVI框架是在MVVM框架的基数上,规定了数据的单项流动,类似于Flutter、Compose等主流框架的写法,非常适合在UI展示的场景。
每个框架有每个框架的特点和使用场景,找到合适框架才是最好的框架
网友评论