code小生,一个 Android 领域技术分享平台
作者:Ruheng链接:https://www.jianshu.com/p/e219ebd1572a声明:本文是 Ruheng 原创,转载等请联系作者获得授权。
前言
已经使用了一段时间的MVP模式,今天再以google官方的安卓架构示例todo-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的实现较为简单,model、view和presenter各个职责明确,便于扩展维护。contract契约类的出现,使得model和presenter结构更加清晰明了。Activity和Fragment的配合,使得Activity职能更为简化,同时View的实现更加灵活。
MVP相关