浅析Android的MVI框架,卷王的日常

作者: 谁动了我的代码 | 来源:发表于2022-10-09 17:33 被阅读0次

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层交互的部分。

  1. Model层承载UI状态,并暴露出ViewState供View订阅,ViewState是个data class,包含所有页面状态。
  2. 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()
}
  1. 我们这里ViewState只定义了两个,一个是请求状态,一个是页面数据。
  2. 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
    }
}

如上所示:

  1. 我们只需定义ViewState与ViewEvent两个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 使用 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展示的场景。

每个框架有每个框架的特点和使用场景,找到合适框架才是最好的框架

相关文章

网友评论

    本文标题:浅析Android的MVI框架,卷王的日常

    本文链接:https://www.haomeiwen.com/subject/qgogartx.html