TODO-MVP 是 Android Architecture Blueprints 中讲解的第一种架构,是一个基本的 MVP 架构。
APP 的入口是 TasksActivity。
它主要包含有两个成员:
MVP 是如何建立起关系来的?
首先,M 只在 P 中使用,与 V 无关,因此 M 只要传入 P 中即可。
P 与 V 之间的关系是这样的:V 和 P 互相保存对方的实例。V 在需要进行数据操作逻辑的时候不自己做,而是交给 P 来做,P 完成之后调用 V 中的方法实现界面更新。
来看对应的代码如下:
首先创建 TasksFragment,并将 Fragment 添加到 Activity 中:
TasksFragment tasksFragment = (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (tasksFragment == null) { // Create the fragment tasksFragment = TasksFragment.newInstance(); ActivityUtils.addFragmentToActivity( getSupportFragmentManager(), tasksFragment, R.id.contentFrame); }
这里用到一个工具类 ActivityUtils 的函数 addFragmentToActivity,实现为:
public static void addFragmentToActivity (@NonNull FragmentManager fragmentManager, @NonNull Fragment fragment, int frameId) { checkNotNull(fragmentManager); checkNotNull(fragment); FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.add(frameId, fragment); transaction.commit(); }
这里插一句与主题无关的话,一个创建 Fragment,Google 写得如此严谨,还有向 checkNotNull 这种第一次见到函数,我还是得学习一个啊。
创建 M 和创建 P 是在一句话里完成的:
// Create the presenter mTasksPresenter = new TasksPresenter( Injection.provideTasksRepository(getApplicationContext()), tasksFragment);
其中:
Injection.provideTasksRepository(getApplicationContext())
这一句就创建了 M 数据源,这里用到了依赖注入,这里的依赖不是靠框架完成的,手动指定的。这里就不展开了,总之这句返回一个 TasksRepository 实例。
P 和 V 相互保存对方的实例发生在 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); }
第一句是将 M 存进 P,第二句是将 V 存进 P,第三句是将 P 存进 V。
在不使用 MVP 的传统结构当中,Activity / Fragment 除了界面的显示逻辑之外,往往还需要实现判断逻辑、数据加载逻辑,就导致结构非常复杂,不好维护。
在 MVP 结构当中,V 中只保留与界面显示有关的逻辑,将判断逻辑和数据加载等都移到 P 中去,这样就实现了一个拆分,使得结构变清晰,因此可维护性就提高了。
对于 TasksActivity 这个 TODO list 应用的主界面而言,V 和 P 具体应该如何划分呢?
下图是 TasksActivity 的界面:

从界面中,我们可以看出 TasksActivity 具备以下功能:
对于界面上的这些功能,需要进一步拆分,将数据有关的放入 P,将显示有关的留在 V,还有一点要强调的是, P 需要绑定生命周期,即在 P 中创建同名方法,在 V 的生命周期中调用,这样保证 P 中的数据源响应生命周期,能及时更新状态 。
上一节中总结了 TasksActivity 的功能,并思考如何将功能分别拆放到 V、P 中。
TasksContract 接口中就存放了拆分的结果,它包含两个接口 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(); } }
这个接口是 MVP 概念中很重要的一个部分。它相当于对一个页面显示、加载数据的一个大纲。在实际的开发过程中,设计好一个界面的原型后,就应该来构思这个接口,思考这个界面都要现实哪些东西,要加载哪些数据,如何进行拆分,先把这个接口写出来。
有了这个接口之后,TasksFragment 实现TasksContract.View,TasksPresenter 实现 TasksContract.Presenter。有一点要注意的是,V 和 P 是紧密耦合在一起的,两者之间会经常相互调用。
至此,本文从 TasksActivity 的代码入手,主要分析了 MVP 中 V 与 P 的关系。在后续的文章中,我会继续对 TODO-MVP 这个官方的 MVP 示范项目进行分析。