Android MVVM模式
MVVM 模式
MVVM(Model-View-ViewModel)最早于 2005 年被微软的 WPF 和 Silverlight 的架构师 John Gossman 提出。将 Presenter 改为 ViewModel,其和 MVP 类似,不同的是 ViewModel 跟 Model 和 View 进行双向绑定,当 View 发生改变时,ViewModel 通知 Model 进行更新数据,同理 Model 数据更新后,ViewModel 通知 View 更新。并且在 2015 年 Google I/O 大会中,发布了 MVVM 的支持库 Data Binding。
简单来讲,MVVM 架构可以将程序结构主要分为 3 部分:
- Model 是数据模型部分
- View 是界面展示部分
- ViewModel 则比较特殊,可将它理解成一个连接数据模型和界面展示的桥梁,从而实现让业务逻辑和界面展示分离的程序结构设计。ViewModel中有一个Binder,在不同系统中的MVVM开发模式中对Binder有不同的实现,比如前端开发中的Vue.js或iOS开发中的RAC,而在Android开发中充当Binder角色的则是Jetpack组件中的DataBinding,Binder的作用就是替代MVP中Presenter层的“中间人”角色。
而一个优秀的项目架构除了会包含以上 3 部分内容以外,还应该包含仓库、数据源等。一个简单易懂的 MVVM 项目架构如下:
- UI 控制层:包含了平时写的 Activity、Fragment、布局文件等与界面相关的东西。
- ViewModel 层:用于持有和 UI 元素相关的数据,以保证这些数据在屏幕旋转时不会丢失,并且还要提供接口给 UI 控制层调用以及和仓库层进行通信。
- 仓库层:主要工作是判断调用方请求的数据应该是从本地数据源中获取还是从网络数据源中获取,并将获取到的数据返回给调用方。本地数据源可以使用数据库等持久化技术来实现,而网络数据源则通常使用 Retrofit 访问服务器提供的 WebService 接口来实现。
- 本地数据源(model)
- 持久化文件
- 网络数据源(model)
- Webservice
- 本地数据源(model)
注意:引用持有都是单向的,并且不能跨层持有。比如,UI 控制层会持有 ViewModel 层的引用,反之则不行,并且 UI 控制层也不能持有仓库层的引用。谨记每一层的组件都只能与它相邻的组件进行交互。
以《第一行代码(第 3 版)》的项目举例学习:
为了让项目能够有更好的结构,一般会新建几个包:
- logic:用于存放业务逻辑相关的代码
- dao:存放数据访问对象
- model:存放对象模型
- network:存放网络相关的代码
- ui:用于存放界面展示相关的代码,place 和 weather 两个子包,分别对应 SunnyWeather 中的两个主要界面。
- place(搜索全球城市数据)
- weather(显示天气信息)
开发流程:
搜索全球城市数据:实现过程应该主要分为逻辑层实现和 UI 层实现两部分
实现逻辑层代码:
- 首先定义数据模型,logic/model 下新建 PlaceResponse 文件。
- 定义用于访问网络的接口,logic/network 下新建 PlaceService 接口。(Retrofit 的接口文件)
- 创建 Retrofit 构造器,logic/network 包下新建 ServiceCreator 单例类。
- 定义统一的网络数据源访问入口,对所有网络请求的 API 进行封装,logic/network 包下新建 SunnyWeatherNetwork 单例类。
- logic 包下新建 Repository 单例类,作为仓库层的统一封装入口。
- 定义 ViewModel 层,在 ui/place 包下新建 PlaceViewModel 。
实现 UI 层代码:
- 编写布局文件,RecyclerView 的 item 布局,适配器。
- ui/place 包下新建 PlaceFragment,并将之添加到 MainActivity 中。
- MainActivity 为搜索页面。
显示天气信息:实现过程应该主要分为逻辑层实现和 UI 层实现两部分
实现逻辑层代码:
- 首先定义数据模型,logic/model 下新建 RealtimeResponse 文件。(实时天气)
- 首先定义数据模型,logic/model 下新建 DailyResponse 文件。(未来天气)
- 定义好数据模型好,进行网络层相关的的代码编写
- logic/network 包下新建 WeatherService 接口。(Retrofit 的接口文件)
- 在 SunnyWeatherNetwork 中对新增的 WeatherService 接口进行封装。
- 然后应该去仓库层进行相关代码的实现了,修改 Repository 中代码。
- 定义 ViewModel 层,在 ui/weather 包下新建 WeatherViewModel 。
实现 UI 层代码
- ui/weather 包下新建 WeatherActivity
记录选中的城市:
- logic/dao 包下新建 PlaceDao 单例类,操作 SharedPreferences 存储数据。
- 在仓库层进行实现,修改 Repository 中的代码。
- 这几个接口的业务逻辑是和 PlaceViewModel 相关的,因此在 PlaceViewModel 中再进行一层封装。
- 并在 PlaceAdapter 中进行保存操作,在PlaceFragment 中进行读取操作。
最终的项目结构如下:
备注
参考资料:
传送门:GitHub