现在从事Android开发多少都要懂点架构知识,从MVC、MVP再到MVVM,想必大家对于其各自的优缺点早已如数家珍。今天介绍的MVI与MVVM非常接近,可以针对性地弥补MVVM中的一些缺陷
何为MVI?
MVI即Model-View-Intent,它受Cycle.js前端框架的启发,提倡一种单向数据流的设计思想,非常适合数据驱动型的UI展示项目:
- Model: 与其他MVVM中的Model不同的是,MVI的Model主要指UI状态(State)。当前界面展示的内容无非就是UI状态的一个快照:例如数据加载过程、控件位置等都是一种UI状态
- View: 与其他MVX中的View一致,可能是一个Activity、Fragment或者任意UI承载单元。MVI中的View通过订阅Intent的变化实现界面刷新(不是Activity的Intent、后面介绍)
- Intent: 此Intent不是Activity的Intent,用户的任何操作都被包装成Intent后发送给Model进行数据请求
单向数据流
用户操作以Intent的形式通知Model => Model基于Intent更新State => View接收到State变化刷新UI。
数据永远在一个环形结构中单向流动,不能反向流动:
这种单向数据流结构的MVI有什么优缺点呢?
- 优点
+ UI的所有变化来自State,所以只需聚焦State,架构更简单、易于调试
+ 数据单向流动,很容易对状态变化进行跟踪和回溯
+ state实例都是不可变的,确保线程安全
+ UI只是反应State的变化,没有额外逻辑,可以被轻松替换或复用
- 缺点
+ 所有的操作最终都会转换成State,所以当复杂页面的State容易膨胀
+ state是不变的,每当state需要更新时都要创建新对象替代老对象,这会带来一定内存开销
+ 有些事件类的UI变化不适合用state描述,例如弹出一个toast或者snackbar
talk is cheap, show me the code。
我们通过一个Sample看一下如何快速搭建一个MVI架构的项目。
代码示例
代码结构如下:
Sample中的依赖库
1 | csharp复制代码// Added Dependencies |
代码中使用以下API进行请求
1 | kotlin复制代码https://reqres.in/api/users |
将得到结果:
- 数据层
1.1 User
定义User
的data class
1 | kotlin复制代码package com.my.mvi.data.model |
1.2 ApiService
定义ApiService,getUsers
方法进行数据请求
1 | kotlin复制代码package com.my.mvi.data.api |
1.3 Retrofit
创建Retrofit实例
1 | kotlin复制代码 |
1.4 Repository
定义Repository,封装API请求的具体实现
1 | kotlin复制代码package com.my.mvi.data.repository |
- UI层
Model定义完毕后,开始定义UI层,包括View、ViewModel以及Intent的定义
2.1 RecyclerView.Adapter
首先,需要一个RecyclerView来呈现列表结果,定义MainAdapter如下:
1 | kotlin复制代码package com.my.mvi.ui.main.adapter |
item_layout.xml
1 | xml复制代码<?xml version="1.0" encoding="utf-8"?> |
2.2 Intent
定义Intent用来包装用户Action
1 | kotlin复制代码package com.my.mvi.ui.main.intent |
2.3 State
定义UI层的State结构体
1 | kotlin复制代码sealed class MainState { |
2.4 ViewModel
ViewModel是MVI的核心,存放和管理State,同时接受Intent并进行数据请求
1 | kotlin复制代码package com.my.mvi.ui.main.viewmodel |
我们在handleIntent
中订阅userIntent
并根据Action类型执行相应操作。本case中当出现FetchUser
的Action时,调用fetchUser
方法请求用户数据。用户数据返回后,会更新State,MainActivity
订阅此State并刷新界面。
2.5 ViewModelFactory
构造ViewModel需要Repository,所以通过ViewModelFactory注入必要的依赖
1 | kotlin复制代码class ViewModelFactory(private val apiService: ApiService) : ViewModelProvider.Factory { |
2.6 定义MainActivity
1 | kotlin复制代码package com.my.mvi.ui.main.view |
MainActivity中订阅mainViewModel.state
,根据State处理各种UI显示和刷新。
activity_main.xml:
1 | xml复制代码<?xml version="1.0" encoding="utf-8"?> |
如上,一个完整的MVI项目就完成了。
最后
MVI在MVVM的基础上,规定了数据的单向流动和状态的不可变性,这类似于前端的Redux思想,非常适合UI展示类的场景。MVVM也好,MVI也好都不是架构的最终形态,世界上没有完美的架构,要根据项目情况选择适合的架构进行开发。
本文转载自: 掘金