Android Architecture Blueprints 学习之 TODO-MVP(一)

TODO-MVP 是 Android Architecture Blueprints 中讲解的第一种架构,是一个基本的 MVP 架构。

TasksActivity

APP 的入口是 TasksActivity。

它主要包含有两个成员:

  • TasksFragment:待做清单的主界面(V
  • TasksPresenter:对应的 Presenter(P
  • TasksRepository: 数据源(M

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。

TasksActivity V 与 P 的功能划分

在不使用 MVP 的传统结构当中,Activity / Fragment 除了界面的显示逻辑之外,往往还需要实现判断逻辑、数据加载逻辑,就导致结构非常复杂,不好维护。

在 MVP 结构当中,V 中只保留与界面显示有关的逻辑,将判断逻辑和数据加载等都移到 P 中去,这样就实现了一个拆分,使得结构变清晰,因此可维护性就提高了。

对于 TasksActivity 这个 TODO list 应用的主界面而言,V 和 P 具体应该如何划分呢?

下图是 TasksActivity 的界面:

Collage_Foto

从界面中,我们可以看出 TasksActivity 具备以下功能:

  • 点击添加 FAB 进入 AddEditTaskActivity 添加页面
  • 点击 TODO item 进入详情页
  • 点击 TODO item 的勾设为已完成
  • 下拉刷新
  • 过滤菜单:所有、活动状态、已完成
  • 菜单:清除已完成、刷新
  • 添加完成弹出 SnackBar 提示成功
  • 生命周期:onResume、onActivityResult

对于界面上的这些功能,需要进一步拆分,将数据有关的放入 P,将显示有关的留在 V,还有一点要强调的是,P 需要绑定生命周期,即在 P 中创建同名方法,在 V 的生命周期中调用,这样保证 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 示范项目进行分析。



  1. 不知道博主在编译 Android Architecture Blueprints 中todo项目时,有没有遇到问题。我在编译时报错:
    Error:Execution failed for task ':app:transformClassesWithNewClassShrinkerForMockDebug'.
    > /Users/Jack/Library/Android/sdk/tools/proguard/proguard-android.txt line 43:15 extraneous input '[]' expecting ')'
    然而不知道怎么解决。