详解MVI 架构
谷歌悄无声息的替换了官网的架构指南,配合着jetpack组件使用的MVVM一直是谷歌推崇的架构,但是如今谷歌好像也认可了MVI,把官方指南架构替换成了MVI。去Google了一下MVI,貌似国外的文章比咱们的多的多,我相信国内MVI的使用者也会越来越多(卷起来~)
什么是MVI?
M:model,此处的model并不是传统的数据模块,它是指用来存储视图状态UI State的一个模块 。比如请求数据时的loading、请求失败的提示页面等UI层面的变化状态。
V:view,视图模块
I:intent,此处的intent并非是我们页面跳转的intent,而是取起实际意思:意图、目的。用来响应用户在操作屏幕时的动作,用户的意图和目的。比如点击保存按钮或者下拉刷新数据等。
如何用代码实现?
MVI的实现方式目前有两种是需要我们知道的,第一种是对过去MVVM的改造升级,也就是对LiveData的升级。第二种则是使用Kotlin中的Flow去实现。两个版本的区别只是在于数据更新的方式不一样而已,一个使用LiveData,一个则是Flow。
以下是需要提前了解相关组件:
LiveData(属性监听相关的知识)
StatwFlow、MutableStateFlow、Channel
1、如果你的项目目前使用的LiveData,你可以借助LiveData属性监听的功能去实现MVI架构。LiveData的升级这里不做相关的介绍了,后期有空再补充。
参考链接:
Google 推荐使用 MVI 架构?卷起来了~
MVI 架构更佳实践:支持 LiveData 属性监听
2、响应式编程似乎是当下最潮流的编程方式,Google在其极力推荐的开发语言Kotlin中也加入了响应式编程框架Flow,目前看来Flow将逐渐取代Rxjava,毕竟Flow才是Google的亲生的。Rxjava的学习成本相对Flow还是比较高的(mmp,重新学一个框架,没有成本吗。。。)
下面争对每个模块进行代码说明:
Model层
Model是用来存储当前页面需要对用户表达的状态模型。比如Loading ,加载状态。Error数据请求失败时的状态,以及请求数据后,进行页面展示数据的状态Articles。所以他并不是我们平时所理解的存储数据模型的层级。这里model存储的时UI State。(其实我们过去的理解的model是存储的数据的,MVI中的model也差不多,只是换了一种概念,把展示数据这个动作当作存储的对象了,只要涉及到屏幕上发生状态改变的状态,都可以存储在model中)
sealed class ArticleState {
object Idle : ArticleState()
object Loading : ArticleState()
data class Articles(val pagingData: PagingData<ArticleBean.DataX>) : ArticleState()
data class Error(val error: String?) : ArticleState()
}
View层
view层就是视图模块。当然我们还需要在View层做一些基本的工作,获取Intent对象,执行用户意图。对Model中的UI State进行监听,然后刷新屏幕状态。
//通过Intent对象,用户发起获取文章列表的意图
lifecycleScope.launch {
viewModel.articleIntent.send(ArticleIntent.GetArticle)
}
//监听UI State 发生的改变,从而刷新屏幕
lifecycleScope.launch {
//lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {} 此处同步livedata的功能,要用
//androidx.lifecycle:lifecycle-runtime-ktx:2.4.0版本才有改功能 绑定生命周期。activity退到后台时,不去收集数据
//flowWithLifecycle(lifecycle, Lifecycle.State.STARTED).
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.state.collect {
when (it) {
is ArticleState.Idle -> {
Toast.makeText(activity, "Idle", Toast.LENGTH_LONG).show()
}
is ArticleState.Loading -> {
Toast.makeText(activity, "Loading", Toast.LENGTH_LONG).show()
}
is ArticleState.Articles -> {
pagingAdapter.submitData(it.pagingData)
}
is ArticleState.Error -> {
Toast.makeText(activity, it.error, Toast.LENGTH_LONG).show()
}
}
}
}
}
Intent层
Intent表示用户意图。我们可以把当前页面用户可以干的事情封装在一起(其中包含主动和被动的,比如我进入到一个列表页面,不需要用户点击任何按钮,进入页面时我们开发的时候 会主动请求列表接口,这就是用户被动意图。其实某种意义上也是主动的,他自己点进这个页面的)
sealed class ArticleIntent {
object GetArticle : ArticleIntent()
object GotoDetail : ArticleIntent()
//等等。。。。
}
以上就是对MVI三个模块的代码展示。源码已经提交到了码云,有兴趣的小伙伴可以去瞅瞅~
记录学习,仅供参考