google在GitHub上开源了android-architecture项目,包含了MVP、MVVM等架构的示例项目,今天我们从todo‑mvp开始入手,研究里面代码的具体实现
项目地址
应用功能介绍
了解一个项目的主要功能最快的方法就是直接安装,然后运行,就可以知道主要有哪些页面,有哪些功能
todo示例项目是一个代办事项的简单App,有四个页面
- 待办列表页
- 待办详情页
- 待办编辑页
- 待办统计页
项目结构
我们主要研究MVP代码的实现,androidTest、androidTestMock、mock、test下是测试的代码,不在我们本次的讨论范围内,下次会另开一篇文章讨论。
项目分包主要以业务模块来划分
- tasks包:待办列表模块
- taskdetail包:待办详情模块
- addedittask包:新建/编辑待办模块
- statistics包:待办统计模块
- data包:跟MVP中V有关的模块
- util包:工具模块
- BaseView接口:MVP中V的基础接口
- BasePresenter包接口:MVP中P的基础接口
我们先看下BaseView的定义,每一个具体的业务模块的View都要实现该接口,通过setPresenter方法持有Presenter的对象引用,然后通过Presenter对象调用具体的业务逻辑
1 | public interface BaseView<T> { |
我们再看下BasePresenter的定义,每一个具体业务模块的Presenter都需要实现该接口,需实现start()方法做一些初始化的业务,而该方法一般是在Fragment的onResume中调用(单不是绝对的,视具体业务而定)
1 | public interface BasePresenter { |
MVP架构概览
如下图所示,我们具体展开每一个业务模块,可以发现每个模块的设计都是一样的,主要有四个类型的类
- Contract:合同类,这是一个接口,里面又定义了两个子接口View和Presenter
- Activity:只是一个Fragment的容器,具体的View实现交个Fragment
- Fragment:实现Contract中定义的View接口,承担View的角色,负责页面的显示刷新交互,持有Presenter的引用,调用Presenter的相关业务实现
- Presenter:实现Contract中定义的Presenter接口,承担Presenter角色,负责具体的业务逻辑,在Presenter中会可能会调用Model对数据进行操作,然后通过持有的View对象的引用回调View,操作更新页面
V和P具体代码实现
由于每一个具体业务模块的实现代码都大同小异,我们选取待办编辑这一业务模块来看看具体的实现代码
待办列表模块的业务功能比较多,代码量也最多,为了快速入门,我们选择待办编辑代码量比较适中,也不会像待办统计也代码量太少。
待办编辑模块主要有两个功能,一个是新建待办然后编辑保存,一个是编辑已有的待办然后保存
待办编辑模块主要有如下四个类,我们按顺序讲解。
- AddEditTaskContract
- AddEditTaskActivity
- AddEditTaskFragment
- AddEditTaskPresenter
AddEditTaskContract
Contract定义了View和Presenter之间的一组协议
View继承BaseView,负责UI的操作更新
Presenter继承BasePresenter,负责具体的业务逻辑,有可能会调用Model的功能
1 | public interface AddEditTaskContract { |
AddEditTaskActivity
Activity的功能比较简单,主要是负责加载Fragment和创建Presenter对象
不承担View和Presenter的功能
1 | public class AddEditTaskActivity extends AppCompatActivity { |
AddEditTaskFragment
AddEditTaskFragment功能很简单,就是编辑待办事项,可以是编辑新建的待办,也可以是编辑已有的待办,然后最后保存返回待办列表页面。
AddEditTaskFragment通过setPresenter持有AddEditTaskPresenter对象示例
在onResume的地方调用Presenter请求已有的任务(如果是已有任务的情况下)
在右下角完成按钮点击的时候调用Presenter的saveTask()方法保存任务
View的主要功能就是:
- 事件触发后调用Presenter处理具体的业务
- 实现Contract中定义的方法操作显示UI(等待Presenter业务处理完的回调)
1 | public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View { |
AddEditTaskPresenter
AddEditTaskPresenter实现了AddEditTaskContract.Presenter中定义的接口
在View中触发事件,然后为了View跟Model层的解耦,甩锅给Presenter让Presenter调用Model执行一些增删改查等操作(本地数据库操作或者网络请求操作),最后再通过Presenter回调View层处理更新UI。整个过程View跟Model是解耦的。
所以Presenter就是View和Model之间的桥梁,是个跑腿的,举个栗子:
View说:喂,Presenter,去Model那边帮我买一瓶酱油回来
Presenter:屁颠屁颠的跑去Model那边买了瓶酱油,然后再跑回去拿给View
View:拿到酱油后炒了一盘炒饭发到朋友圈显示出来
1 | /** |
Model设计
本示例中Mode层在data包下,又分为local和remote两个包。
local表示本地数据库数据
remote表示远程服务端数据,但是本示例并未真正实现服务端接口的请求,只是用一个LinkedHashMap增删改查待办任务,然后用Handler.postDelay来模拟异步请求
TaskDatasource定义了数据源的接口,包括获取所有待办事项、获取单个待办事项、创建待办事项、完成待办事项等
TaskLocalDataSource实现了TaskDatasource,实现了本地的数据操作
TaskRemoteDataSource实现了TaskDataSource,模拟了网络异步的数据操作
TaskRepository代表的就是View层,Presenter中持有的View对象就是TaskRepository,TaskRepository负责提供数据和对数据进行操作,然后把结果返回给Presenter,Presenter再回调View更新视图。
TaskRepository也实现了TaskDatasource接口,内部同时持有TaskLocalDataSource和TaskRemoteDataSource两种数据源,同时还有一个Map内存缓存,对待办数据进行增加、删除、修改等操作是会同时对内存缓存、TaskLocalDataSource、TaskRemoteDataSource三种数据源进行操作。
总结
盗用一张百科的图片,如有侵权请联系删除
- View和Model彻底解耦
- Presenter是View和Model之间的桥梁
一个完整流程包含以下四个步骤:
- View中事件触发
- Presenter调用Model请求处理数据
- Model处理完数据返回给Presenter
- Presenter回调View更新UI