用MVP模式开发Andorid应用

用MVP模式开发Android应用

概述

之前学习MVP的时候写了这篇文章,一直躺在硬盘里,今天整理资料的时候发现了,权当是一个学习记录,希望能为准备学习MVP的小伙伴提供一个思路!

MVP去年就很火了,但是担心掌握不好一直没有用。直到这一次面试面试官问了这个,当时对这个模式理解的不是很好,于是回来补习了一下。

学习之后我们要搞清楚几个问题:

1 什么是MVP模式?

2 他的出现解决了什么问题?

3 如何搭建一个MVP应用?

4 使用他会造成哪些新的问题?

1 什么是MVP模式

是一种软件架构模式,为了解耦,减少代码冗余。

Model 依然是业务逻辑和实体模型(M)

View 对应于Activity,负责View的绘制以及与用户交互(V)

Presenter 负责完成View于Model间的交互(P)

简单解释下这张图,View(也可以理解成Activity)中持有Presenter的实例,Presenter中持有View的接口和Model的实例。这就不难理解了,View和Model可以通过Presenter交互。并且,因为是接口,变换View也很容易,只要实现了IView接口,完全不会影响到Model的逻辑。这样就实现了解耦。

2 他的出现解决了什么问题

如果看明白了上图,就能明白它能做什么了。简而言之:解耦,减少冗余,灵活,结构清晰。

做过大型项目的哥们都知道,动辄都是几百上千的类,有的Activity中逻辑太多,后期看起来简直头疼。MVP的出现把Activity中的逻辑抽取出来,这样看起来就会简单很多。逻辑一目了然。

3 如何搭建一个MVP应用

我们这里以Google 的sample为例。

老司机自行fork:https://github.com/googlesamples/android-architecture

我们以最简单的todo-mvp分支下的例子来讲怎么实现一个MVP应用。

首先来看一下他的项目和项目工程结构。

可以看出来是一个类似日历记事本的一个项目。功能比较简单。

添加任务;任务统计;按照状态切换任务;编辑任务。

我们就把首页挑出来分析一下。

View(V)

MVP中的View(V),负责绘制UI元素、与用户进行交互(在Android中体现为Activity),本例中指的就是首页这个TasksActivity;

他有显示所有任务;显示加载loading;显示筛选菜单;添加任务这些功能。google的工程师想的更加全面,还有根据任务ID跳转详情;snack在选择完成时的提示;在选择进行中的提示;在选择清空时的提示;首页TextView在没有任务时的文本;在切换不同Filter时的文本。还有很多不列举了,一会看一下接口里面定义的方法。将这些页面上有数据操作的功能都抽取出来放到IView接口中。

  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();
    }

然后让TasksActivity中的TasksFragment实现这个接口,并实现上述定义的这些方法。V 就定义好了。

Module(M)

接下来看一下Module(M),负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合);简单来说就是处理业务逻辑,本例中指得是TasksRepository,我们再来分析一下它会有哪些操作!

TasksDataSource接口中的定义,查找所有任务;根据ID查找任务;添加任务;编辑任务;删除任务;看下接口。

public interface TasksDataSource {

    interface LoadTasksCallback {

        void onTasksLoaded(List<Task> tasks);

        void onDataNotAvailable();
    }

    interface GetTaskCallback {

        void onTaskLoaded(Task task);

        void onDataNotAvailable();
    }

    void getTasks(@NonNull LoadTasksCallback callback);

    void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);

    void saveTask(@NonNull Task task);

    void completeTask(@NonNull Task task);

    void completeTask(@NonNull String taskId);

    void activateTask(@NonNull Task task);

    void activateTask(@NonNull String taskId);

    void clearCompletedTasks();

    void refreshTasks();

    void deleteAllTasks();

    void deleteTask(@NonNull String taskId);
}

通过上述对View和Module的接口定义不难看出来,View主要操作了一些UI上的显示,Module主要负责了数据上的逻辑处理。业务被很好的分层了。

Presenter(P)

最后我们来看一下Presenter(P),作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。也可以理解为他促成了Module和View双方的通信。本例以TasksPresenter来解说。

在Presenter中要持有Module与View。看到这句话应该就能明白他是如何起到桥梁的作用了。

同样的,为了结构统一和以后的拓展性,Google工程师这里也采用了接口的定义方式。先贴出接口自己理解。

 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();
    }

我们以loadTasks为例来感受下他是如何发挥纽带作用的。

首先用户在Activity中下拉刷新会触发LoadTasks动作。

接下来会调用TasksPresenter的loadTasks方法(
这里的mPresenter就是TasksPresenter的实例)。我们这里只摘要核心代码,想看源码的去GIT里面看。


    /**
     * @param forceUpdate   Pass in true to refresh the data in the {@link TasksDataSource}
     * @param showLoadingUI Pass in true to display a loading icon in the UI
     */
    private void loadTasks(boolean forceUpdate, final boolean showLoadingUI) {
        if (showLoadingUI) {
            mTasksView.setLoadingIndicator(true);//显示加载的Loading
        }
        if (forceUpdate) {
            mTasksRepository.refreshTasks();//刷新任务
        }

        mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() {//查找任务
            @Override
            public void onTasksLoaded(List<Task> tasks) {//请求成功的回调
                if (!mTasksView.isActive()) {
                    return;
                }
                if (showLoadingUI) {
                    mTasksView.setLoadingIndicator(false);
                }

                if (tasks.isEmpty()) {
                    // Show a message indicating there are no tasks for that filter type.
                    processEmptyTasks();
                } else {
                    // Show the list of tasks
                    mTasksView.showTasks(tasks);
                    // Set the filter label's text.
                    showFilterLabel();
                }
            }
        });
    }

大致逻辑就是通过Presenter中的Module实例调用查找任务,并在收到回调之后调用Presenter中的View 实例变换UI。

利用接口编程的好处就是灵活,比如说我这里想换一套UI但是业务逻辑不变,我只需要在写一个Activity并且实现IView接口即可,完全不用担心逻辑。修改逻辑也一样,只用增加接口方法,就可以达到逻辑的变换,不用担心UI受到影响。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值