前言

Android开发倒退到明天曾经相当成熟了,各种架构大家也都耳熟能详,如MVC,MVP,MVVM等,其中MVVM更是被官网举荐,成为Android开发中的显学。
不过软件开发中没有银弹,MVVM架构也不是尽如人意的,在应用过程中也会有一些不太不便之处,而MVI能够很好的解决一部分MVVM的痛点。
本文次要包含以下内容

  1. MVC,MVP,MVVM等经典架构介绍
  2. MVI架构到底是什么?
  3. MVI架构实战
须要重点指出的是,题目中说MVI架构是MVVM的进阶版是指MVIMVVM十分类似,并在其根底上做了肯定的改进,并不是说MVI架构肯定比MVVM适宜你的我的项目
各位同学能够在剖析比拟各个架构后,抉择适合我的项目场景的架构

经典架构介绍

MVC架构介绍

MVC是个古老的Android开发架构,随着MVPMVVM的风行曾经逐步退出历史舞台,咱们在这里做一个简略的介绍,其架构图如下所示:

MVC架构次要分为以下几局部

  1. 视图层(View):对应于xml布局文件和java代码动静view局部
  2. 管制层(Controller):次要负责业务逻辑,在android中由Activity承当,同时因为XML视图性能太弱,所以Activity既要负责视图的显示又要退出管制逻辑,承当的性能过多。
  3. 模型层(Model):次要负责网络申请,数据库解决,I/O的操作,即页面的数据起源

因为androidxml布局的功能性太弱,Activity实际上负责了View层与Controller层两者的工作,所以在androidmvc更像是这种模式:

因而MVC架构在android平台上的次要存在以下问题:

  1. Activity同时负责ViewController层的工作,违反了繁多职责准则
  2. Model层与View层存在耦合,存在相互依赖,违反了最小常识准则

MVP架构介绍

因为MVC架构在Android平台上的一些缺点,MVP也就应运而生了,其架构图如下所示 :

MVP架构次要分为以下几个局部

  1. View层:对应于ActivityXML,只负责显示UI,只与Presenter层交互,与Model层没有耦合
  2. Presenter层: 次要负责解决业务逻辑,通过接口回调View
  3. Model层:次要负责网络申请,数据库解决等操作,这个没有什么变动

咱们能够看到,MVP解决了MVC的两个问题,即Activity承当了两层职责与View层与Model层耦合的问题

MVP架构同样有本人的问题

  1. Presenter层通过接口与View通信,实际上持有了View的援用
  2. 然而随着业务逻辑的减少,一个页面可能会非常复杂,这样就会造成View的接口会很宏大。

MVVM架构介绍

MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致。
惟一的区别是,它采纳双向数据绑定(data-binding):View的变动,主动反映在 ViewModel,反之亦然
MVVM架构图如下所示:

能够看出MVVMMVP的次要区别在于,你不必去被动去刷新UI了,只有Model数据变了,会主动反映到UI上。换句话说,MVVM更像是自动化的MVP

MVVM的双向数据绑定次要通过DataBinding实现,不过置信有很多人跟我一样,是不喜爱用DataBinding的,这样架构就变成了上面这样

  1. View察看ViewModle的数据变动并自我更新,这其实是繁多数据源而不是双向数据绑定,所以其实MVVM的这一大个性我其实并没有用到
  2. View通过调用ViewModel提供的办法来与ViewMdoel交互

小结

  1. MVC架构的次要问题在于Activity承当了ViewController两层的职责,同时View层与Model层存在耦合
  2. MVP引入Presenter层解决了MVC架构的两个问题,View只能与Presenter层交互,业务逻辑放在Presenter
  3. MVP的问题在于随着业务逻辑的减少,View的接口会很宏大,MVVM架构通过双向数据绑定能够解决这个问题
  4. MVVMMVP的次要区别在于,你不必去被动去刷新UI了,只有Model数据变了,会主动反映到UI上。换句话说,MVVM更像是自动化的MVP
  5. MVVM的双向数据绑定次要通过DataBinding实现,但有很多人(比方我)不喜爱用DataBinding,而是View通过LiveData等察看ViewModle的数据变动并自我更新,这其实是繁多数据源而不是双向数据绑定

MVI架构到底是什么?

MVVM架构有什么有余?

要理解MVI架构,咱们首先来理解下MVVM架构有什么有余
置信应用MVVM架构的同学都有如下教训,为了保障数据流的单向流动,LiveData向外裸露时须要转化成immutable的,这须要增加不少模板代码并且容易忘记,如下所示

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    //...}

如上所示,如果页面逻辑比较复杂,ViewModel中将会有许多全局变量的LiveData,并且每个LiveData都必须定义两遍,一个可变的,一个不可变的。这其实就是我通过MVVM架构写比较复杂页面时最好受的点。
其次就是View层通过调用ViewModel层的办法来交互的,View层与ViewModel的交互比拟扩散,不成体系

小结一下,在我的应用中,MVVM架构次要有以下有余

  1. 为保障对外裸露的LiveData是不可变的,须要增加不少模板代码并且容易忘记
  2. View层与ViewModel层的交互比拟扩散零乱,不成体系

MVI架构是什么?

MVIMVVM 很类似,其借鉴了前端框架的思维,更加强调数据的单向流动和惟一数据源,架构图如下所示

其次要分为以下几局部

  1. Model: 与MVVM中的Model不同的是,MVIModel次要指UI状态(State)。例如页面加载状态、控件地位等都是一种UI状态
  2. View: 与其余MVX中的View统一,可能是一个Activity或者任意UI承载单元。MVI中的View通过订阅Model的变动实现界面刷新
  3. Intent: 此Intent不是ActivityIntent,用户的任何操作都被包装成Intent后发送给Model层进行数据申请

单向数据流

MVI强调数据的单向流动,次要分为以下几步:

  1. 用户操作以Intent的模式告诉Model
  2. Model基于Intent更新State
  3. View接管到State变动刷新UI。

数据永远在一个环形构造中单向流动,不能反向流动:

下面简略的介绍了下MVI架构,上面咱们一起来看下具体是怎么应用MVI架构的

MVI架构实战

总体架构图

咱们应用ViewModel来承载MVIModel层,总体构造也与MVVM相似,次要区别在于ModelView层交互的局部

  1. Model层承载UI状态,并暴露出ViewStateView订阅,ViewState是个data class,蕴含所有页面状态
  2. View层通过Action更新ViewState,代替MVVM通过调用ViewModel办法交互的形式

MVI实例介绍

增加ViewStateViewEvent

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()}
  1. 咱们这里ViewState只定义了两个,一个是申请状态,一个是页面数据
  2. ViewEvent也很简略,一个简略的密封类,显示ToastSnackbar

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    }}

如上所示

  1. 咱们只需定义ViewStateViewEvent两个State,后续减少状态时在data class中增加即可,不须要再写模板代码
  2. ViewEvents是一次性的,通过SingleLiveEvent实现,当然你也能够用Channel当来实现
  3. 当状态更新时,通过emit来更新状态

View监听ViewState

    private fun initViewModel() {        viewModel.viewStates.observe(this) {            renderViewState(it)        }        viewModel.viewEvents.observe(this) {            renderViewEvent(it)        }    }

如上所示,MVI 应用 ViewStateState 集中管理,只须要订阅一个 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通过ActionViewModel交互,通过 Action 通信,有利于 ViewViewModel 之间的进一步解耦,同时所有调用以 Action 的模式汇总到一处,也有利于对行为的集中剖析和监控

总结

本文次要介绍了MVC,MVP,MVVMMVI架构,目前MVVM是官网举荐的架构,但依然有以下几个痛点

  1. MVVMMVP的次要区别在于双向数据绑定,但因为很多人(比方我)并不喜爱应用DataBindg,其实并没有应用MVVM双向绑定的个性,而是繁多数据源
  2. 当页面简单时,须要定义很多State,并且须要定义可变与不可变两种,状态会以双倍的速度收缩,模板代码较多且容易忘记
  3. ViewViewModel通过ViewModel裸露的办法交互,比拟零乱难以保护

MVI能够比拟好的解决以上痛点,它次要有以下劣势

  1. 强调数据单向流动,很容易对状态变动进行跟踪和回溯
  2. 应用ViewStateState集中管理,只须要订阅一个 ViewState 便可获取页面的所有状态,绝对 MVVM 缩小了不少模板代码
  3. ViewModel通过ViewStateAction通信,通过浏览ViewStateAciton 定义就能够理清 ViewModel 的职责,能够间接拿来作为接口文档应用。

当然MVI也有一些毛病,比方

  1. 所有的操作最终都会转换成State,所以当简单页面的State容易收缩
  2. state是不变的,因而每当state须要更新时都要创立新对象代替老对象,这会带来肯定内存开销

软件开发中没有银弹,所有架构都不是完满的,有本人的实用场景,读者可依据本人的需要抉择应用。
但通过以上的剖析与介绍,我置信应用MVI架构代替没有应用DataBindingMVVM是一个比拟好的抉择~

相干视频举荐:

【2021最新版】Android studio装置教程+Android(安卓)零基础教程视频(适宜Android 0根底,Android初学入门)含音视频_哔哩哔哩_bilibili

Android架构设计原理与实战——Jetpack联合MVP组合利用开发一个优良的APP!_哔哩哔哩_bilibili

Android进阶必学:jetpack架构组件—Navigation_哔哩哔哩_bilibili

Android进阶零碎学习——Jetpack先天优良的基因能够防止数据内存透露_哔哩哔哩_bilibili