code 小生, 一个 Android 领域技术分享平台
作者: Ruheng 链接: https://www.jianshu.com/p/e219ebd1572a 声明: 本文是 Ruheng 原创, 转载等请联系作者获得授权
前言
已经使用了一段时间的 MVP 模式, 今天再以 google 官方的安卓架构示例 todo-mvp 为例, 再次学习 MVP 模式
一 MVP 简介
MVP 简介
Model: 数据层, 负责与网络层和数据库层的逻辑交互
View: UI 层, 显示数据, 并向 Presenter 报告用户行为
Presenter: 从 Model 拿数据, 应用到 UI 层, 管理 UI 的状态, 响应用户的行为
二 MVP 优势
在学习 todo-mvp 之前, 先了解一下 MVP 的优势
分离了视图逻辑和业务逻辑, 降低了耦合
Activity 只处理生命周期的任务, 代码变得更加简洁
视图逻辑和业务逻辑分别抽象到了 View 和 Presenter 的接口中, 提高代码的阅读性
Presenter 被抽象成接口, 可以有多种具体的实现, 所以方便进行单元测试
把业务逻辑抽到 Presenter 中去, 避免后台线程引用着 Activity 导致 Activity 的资源无法被系统回收从而引起内存泄露和 OOM
三 todo-mvp 结构解析
1. 项目结构
从上图可以看出, todo-mvp 是按照功能模块划分的
其中 tasks, taskdetail, addedittask, statistics 是四个业务模块
data 是数据模块, 其中具体的类 TasksRepository 担任 Model 层, 负责远程和本地数据的获取
BasePresenter 和 BaseView 是 presenter 和 view 的基类, 在具体模块承担实际功能最后, util 是工具类集合
2. 具体解析在 todoapp 中, MVP 的具体结构如下图所示:
下面以 tasks 模块具体阐述上述图片中的实际作用关系
基类
public interface BasePresenter { void start();}
其中 start() 方法的作用是 presenter 开始获取数据并调用 view 中方法改变界面显示, 其调用时机是在 Fragment 类的 onResume 方法中
public interface BaseView<T> { void setPresenter(T presenter);}
其中 setPresenter() 方法作用是在将 presenter 实例传入 view 中, 其调用时机是 presenter 实现类的构造函数中
契约类
与之前使用的 MVP 实现不同, 官方的实现中加入了契约类来统一管理 view 与 presenter 的所有的接口, 这种方式使得 view 与 presenter 中有哪些功能, 一目了然, 维护起来也方便, 同时使得 view 与 presenter 一一对应, 并有效地减少类的数目
- public interface TasksContract { interface View extends BaseView<Presenter> { void setLoadingIndicator(boolean active); void showTasks(List<Task> tasks); void showAddTask(); void showTaskDetailsUi(String taskId); void showTaskMarkedComplete(); void showTaskMarkedActive(); void showCompletedTasksCleared(); void showLoadingTasksError(); void showNoTasks(); void showActiveFilterLabel(); void showCompletedFilterLabel(); void showAllFilterLabel(); void showNoActiveTasks(); void showNoCompletedTasks(); void showSuccessfullySavedMessage(); boolean isActive(); void showFilteringPopUpMenu(); } interface Presenter extends BasePresenter { void result(int requestCode, int resultCode); void loadTasks(boolean forceUpdate); void addNewTask(); void openTaskDetails(@NonNull Task requestedTask); void completeTask(@NonNull Task completedTask); void activateTask(@NonNull Task activeTask); void clearCompletedTasks(); void setFiltering(TasksFilterType requestType); TasksFilterType getFiltering(); }}
- TasksActivity
Activity 在项目中是一个全局的控制者, 负责创建 view 以及 presenter 实例, 并将二者联系起来
TasksFragment tasksFragment = (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (tasksFragment == null) { // 创建 fragment tasksFragment = TasksFragment.newInstance(); ActivityUtils.addFragmentToActivity( getSupportFragmentManager(), tasksFragment, R.id.contentFrame); } // 创建 presenter,TasksPresenter 是 TasksContract.Presenter 的实现类 // 传入两个参数 //1.TasksRepository 实例, 负责数据层 //2.tasksFragment, 是 TasksContract.View 的实现类, 即 view 实例 mTasksPresenter = new TasksPresenter( Injection.provideTasksRepository(getApplicationContext()), tasksFragment);
其中, 通过实例化 TasksPresenter 时, 传入 tasksFragment, 使得在 TasksPresenter 中拥有 view 实例同时, 在实例化时初始化构造函数, 调用了 setPresenter() 方法, 使得 view 实例中拥有了 presenter 实例对象, 使得两者联系起来
TasksPresenter 构造函数如下所示:
- public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) { mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null"); mTasksView = checkNotNull(tasksView, "tasksView cannot be null!"); mTasksView.setPresenter(this); }
- TasksFragment
将 Fragment 作为 view 层的实现类, 使得 Activity 作为一个全局控制类来创建对象, 而 Fragment 作为 view, 两者各司其职同时, Fragment 比较灵活, 能够方便的处理界面适配的问题
public class TasksFragment extends Fragment implements TasksContract.View { ........... public static TasksFragment newInstance() { return new TasksFragment(); } ........... @Override public void onResume() { super.onResume(); mPresenter.start(); } ........... @Override public void setPresenter(@NonNull TasksContract.Presenter presenter) { mPresenter = checkNotNull(presenter); } ........... @Override public boolean isActive() { return isAdded(); } ...........}
对于 view 的实现 TasksFragment, 只挑一部分重要的方法来看
newInstance () 方法, 实例化 TasksFragment 对象
setPresenter() 方法继承于父类, 通过该方法, view 获得了 presenter 得实例, 从而可以调用 presenter 代码来处理业务逻辑
在 onResume() 中, 调用了 presenter 得 start() 方法, 获取数据并操作 view 界面的显示
isActive() 方法, 通过 isAdded() 判断对应 Activity 是否销毁在 Fragment 在执行异步耗时操作后, 如果调用 Activity 实例, 应当先使用 isActive() 方法加以判断
四总结
通过对 todo-mvp 分析, 再次了解学习了 MVP 从 google 提供的例子中可以看出, MVP 的实现较为简单, modelview 和 presenter 各个职责明确, 便于扩展维护 contract 契约类的出现, 使得 model 和 presenter 结构更加清晰明了 Activity 和 Fragment 的配合, 使得 Activity 职能更为简化, 同时 View 的实现更加灵活
MVP 相关
MVP + 多线程 + 断点续传 实现 app 在线升级库 (手把手教你打造自己的 lib)
一步一步带你认识 MVP+Retrofit+Rxjava 并封装 (二)
来源: https://juejin.im/entry/5ab0f8d15188255572083788