Android MVP 模式使用指南

参考github代码:github
参考博客:Google 官方Android MVP架构实践

简单的以google官方代码和自己的demo记录Android的MVP设计模式。

1.官方MVP模式:

官方MVP的samples的github地址是(https://github.com/googlesamples/android-architecture/)

若水三千,我们先取一瓢。先看看最简单的todo-mvp分支,这是最基础的MVP架构samples。

在我从前认识的MVP模式中,并不存在BasePresenter和BaseView两个文件,只需要针对不同的Presenter和View来定义不同的接口就行了。
来看一下这两个文件中都写了啥:

BasePresenter.java
public interface BasePresenter {
    void start();
}

BaseView.java
public interface BaseView<T> {
    void setPresenter(T presenter);
}

BasePresenter中声明了一个方法,当我们的实现类presenter需要开始进行动作时,事先调用start方法,来进行一些预处理动作。(代码中都是在View的实现类的onResume中调用presenter的start方法)。
因为google的sample中presenter需要做一些画面初始化的操作,所以每个画面都需要这样的方法将数据加载到画面上,所以使用了这个方法来作为初始化方法,我们在实际编码时要根据情况观察是否使用这个模板。

BaseView中声明了一个方法,setPresenter(),这里的设计和我平常的理解有所不同。我平时的想法是在view的onCreate方法中new出presenter实例作为view的成员变量,而google的例子是在activity中使用fragment实例new出presenter实例,但是这个presenter不属于fragment,需要在presenter中调用mTasksView.setPresenter(this)。

TasksActivity.java
@Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        TasksFragment tasksFragment =
                (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
        ...
        // Create the presenter
        mTasksPresenter = new TasksPresenter(
                Injection.provideTasksRepository(getApplicationContext()), tasksFragment);
        ...
    }
TasksPresenter.java
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);
    }

另外,我们发现google提供的包结构与我常用的不同,我们会按照模块来进行划分,例如:view,presenter,util,contract。但是google是将view,presenter,contract放在一起的,仁者见仁吧。

2.我认识的MVP模式:

首先我们会定义一个接口叫做Contract,在其中我们会定义三个接口,分别是Presenter,View和Model的接口,其中View和Model中都只声明了一个方法供presenter调用,而Presenter中声明了两个方法供View和Model调用。

当然,一般在一个app中不可能只有一个Contract,因为我们需要针对不同类型的画面定义不同的画面方法,每个不同类型的画面上的逻辑也不会相同。

接下来我们来说明一下一个正常的mvp模式是如何运作的,我们需要实现一个很简单的功能,登录检查。
(检查用户名和密码是否与我们数据库中的匹配,这里为了简单起见,我们就不再写数据库的操作了,而是直接使用现成的字符串来检查)。

首先,这是我们的Contract部分,因为我们只需要针对一个登录画面,所以我们只需要一个Contract来存放三个接口。

/**
 * This specifies the contract between the view and the presenter.
 */
public interface LoginContract {
    interface Presenter {
        /**
         * login action.
         * @param name user name.
         * @param pwd user password.
         */
        void login(String name, String pwd);

        /**
         * set login result for presenter instance.
         * @param resultCode
         */
        void setLoginResult(int resultCode);
    }

    interface View {
        /**
         * show login result for view instance.
         * @param resultCode login result;
         * {@link LoginPresenter#LOGIN_ERROR}, {@link LoginPresenter#LOGIN_SUCCESS}.
         */
        void showLoginResult(int resultCode);
    }

    interface Model {
        /**
         * login action.
         * @param name user name.
         * @param pwd user password.
         */
        void login(String name, String pwd);
    }
}

接下来使我们的presenter的具体实现类,login方法只是向下调用了model的login方法,有人会觉得这样做很奇怪,不是多此一举吗?其实,Presenter的作用就是一个桥梁,连接View和Presenter的桥梁。

/**
 * Listens to user actions from the UI ({@link com.siw.mvp.view.MainActivity}), retrieves the data and updates the
 * UI as required.
 */
public class LoginPresenter implements LoginContract.Presenter {
    private final static String TAG = LoginPresenter.class.getSimpleName();
    private LoginContract.View mView;
    private LoginContract.Model mModel;
    public static final int LOGIN_ERROR = 0;
    public static final int LOGIN_SUCCESS = 1;

    public LoginPresenter(LoginContract.View view) {
        this.mView = view;
        mModel = new LoginModel(this);
    }

    @Override
    public void login(String name, String pwd) {
        Log.d(TAG, "login() enter, name = " + name + "pwd = " + pwd);
        mModel.login(name, pwd);
    }

    @Override
    public void setLoginResult(int resultCode) {
        Log.d(TAG, "setLoginResult() enter, resultCode = " + resultCode);
        if (null != mView) {
            mView.showLoginResult(resultCode);
        } else {
            Log.e(TAG, "mView = null");
        }
    }
}

最后是Model,这里定义了其实现的接口中的方法,为了方便起见,在Model中的login逻辑处理只用了最简单的字符串比较。

public class LoginModel implements LoginContract.Model {
    private final static String TAG = LoginModel.class.getSimpleName();
    private LoginContract.Presenter mPresenter;

    public LoginModel(LoginContract.Presenter mPresenter) {
        this.mPresenter = mPresenter;
    }

    @Override
    public void login(String name, String pwd) {
        Log.d(TAG, "login() enter");
        if (null == mPresenter) {
            Log.e(TAG, "mPresenter = null");
            return;
        }
        if ("admin".equals(name) && "root".equals(pwd)) {
            mPresenter.setLoginResult(LoginPresenter.LOGIN_SUCCESS);
        } else {
            mPresenter.setLoginResult(LoginPresenter.LOGIN_ERROR);
        }
    }
}

最后聊一聊一个问题,为什么要使用MVP模式,或者说,这种模式的好处在哪?

通过以上两个sample的分析,我们发现,使用MVP模式后,原本会被放在Activity或Fragment中的代码都被抽象出来了,被分成了MVP三个部分。这三个部分其实承担了三个不同的职责,View负责显示以及响应用户操作,Model负责处理逻辑,而Presenter则负责将View和Presenter两层联系起来,就像是一座桥。这样的三个部分的划分,极大的降低了代码之间的耦合度,让代码的执行变得十分清晰明确。

另外,有人想问,中间的Presenter层的作用是什么呢?Presenter的存在进一步分离了上层画面和底层逻辑,在Android中,我们的底层逻辑会更多的使用异步线程的方式执行,那么Presenter就可以承担起一个业务逻辑层的作用,让Model层进行纯粹的底层逻辑的执行。

一般来说,MVP设计模式适合稍微大一些的项目,如果只是简单的小demo,那么没有必要使用MVP。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值