Android 如何运用MVP模式开发(以封装Login模块为例)

前言:

本文将会,向你介绍如何在android开发中运用MVP模式,来解藕并提高代码的复用。现在,如果不采用个什么模式来开发,就像当年不染个五彩缤纷的杀玛特头发一样,显得与时代格格不入。虽然以前没有染发加入杀玛一族,导致追悔莫及。但是,这次得把握住机会,紧追时代潮流,加入MVP模式开发一族(MVP沙文主义:凡是不用MVP模式开发的,都是在耍流氓)。代码注释用英文写的,目的是练习英文。用的都是比较简单的词,不懂百度翻译一下就好。

一、快速入门MVP

这里写图片描述

咱们先来,简单快速的图解一下啥是MVP。MVP三个词,分别代表开发中的三个模块,分别是:

1、M是model数据层。不管M从哪里、怎么拿数据,只管让M提供数据给P就行,用户和V层是感知不到的。
2、V是view视图层。即是与用户直接相交互的部分,只管更新界面,响应用户的操作。V不管数据的处理,有什么数据要获取和保存的,都交给P处理。
3、P是presenter中间者。一个中介,负责剖离V和M,解藕数据和UI。比如说:V要数据,P就去找M拿,拿完,再给V。这样,当M改了,只要修改M与P的协议就行,不会影响到V层。同样地,当V改了,也只要修改V与P的协议就行,不会影响到M层。

:MVP三者之间的联系,通过协议(Java接口),来解藕。
That’s all!这就是MVP浅显易懂的原理。说了这么多,总得有个实例来论证一下吧?Talk is cheap, Show you demo.

二、实战。干大事的人,都是直接先上架构图的(可以点击图片放大查看)。

这里写图片描述

从架构图可以看出,里面最重要的是LoginContact类。因为LoginContact包含了View和Presenter的协议。咱们先来看看这个类,都有些什么:

/**
 * @Author Lyf
 * @CreateTime 2018/2/27
 * @Description A LoginContact defines two protocols for View and Presenter.
 *  Protocols define and limit what View and Presenter can do.
 **/
public class LoginContact {

    public interface View {

        void showLoading();

        void hideLoading();
    }

    public interface Presenter  {

        View getView();

        /**
         * Uses phoneNum and passWord to post a request to remote server.
         *
         * @param phoneNumKey   request name
         * @param phoneNumValue request value
         * @param passWordKey   request name
         * @param passWordValue request value
         */
        void onLoginWithPassWord(@NonNull String phoneNumKey, @NonNull String phoneNumValue,
                                 @NonNull String passWordKey, @NonNull String passWordValue);

        /*
         * LifeCycle of Activity.
         */
        void subscribe();

        void unSubscribe();

    }

}

LoginContact里面的接口View,定义了View能做的事情。这里,一切从简。定义了两个方法,用于显示加载和关闭加载提示框的方法。

  public interface View {

        void showLoading();
        void hideLoading();
    }

咱们再来看看,LoginContact里面的接口Presenter,它定义了Presenter能做的事情:

  public interface Presenter  {

        View getView();

        void subscribe();

        void unSubscribe();

        void onLoginWithPassWord(@NonNull String phoneNumKey, @NonNull String phoneNumValue,
                                 @NonNull String passWordKey, @NonNull String passWordValue);
    }

接口Presenter里的方法也不多,一共只有四个。第一个是获取View,第二、三个是开始与结束,类似onCreate和onDestroy。第四个是用于登录用的。

上面所说的这两接口,就是协议,协议里面的方法,则是协议的条款。这些条款规定了一个类View或一个Presenter能够做什么。采用MVP模式开发的时候,最先要做的,不是直接开始编码,而是要先设计好协议。一切围着协议来,即面向协议开发。有了协议之后,就得开始,创建实现这些协议的类。这里有两个协议,咱们先讨论Presenter的协议。

在这个架构里面,我创建了一个叫AbstractLoginPresenter的抽象类,实现了LoginContact.Presenter协议。那么问题来了,为什么要创建一个抽象类,而不是直接创建一个普通的LoginPresenter类呢?因为,当你有一定的开发经验后,你会发现。每次重构相同项目也好,或是开始一个新的项目也好。登录功能几乎是必须的,相应的,登录时的参数检查也是必须的。你不可能,不检查参数,就直接把一些不合法的参数抛给服务器处理。为了提高代码的复用,这里抽了一个抽象类,用来处理一些重复的动作。比如,检查参数。好了,让我们来看看这个抽象类的代码:

/**
 * @Author Lyf
 * @CreateTime 2018/2/5
 * @Description An AbstractLoginPresenter deals with certain tasks.
 * For instance, initializing an interceptor to check login params.
 **/
public abstract class AbstractLoginPresenter implements LoginContact.Presenter {

    private Application mApplication;

    private LoginContact.View mLoginView;
    private ArrayMap<String, Object> mLoginParams = new ArrayMap<>();
    private LoginInterceptor mLoginInterceptor = new LoginInterceptor();

    public AbstractLoginPresenter(Application mApplication,LoginContact.View mLoginView) {
        this.mLoginView = mLoginView;
        this.mApplication = mApplication;
    }

    // An abstract method which the subclass has to override 
    // and in which posts a real request to remote server.
    public abstract void onLoginWithPassWord(@NonNull ArrayMap<String, Object> mLoginParams);

    @Override
    public void onLoginWithPassWord(@NonNull String phoneNumKey, @NonNull String phoneNumValue,
                                    @NonNull String passWordKey, @NonNull String passWordValue) {

        if ( mLoginInterceptor.checkLoginWithPassWord(mApplication,
                phoneNumValue, passWordValue)) {

            mLoginParams.put(phoneNumKey, phoneNumValue);
            mLoginParams.put(passWordKey, passWordValue);
            onLoginWithPassWord(mLoginParams);
        }

    }

    @Override
    public void subscribe() {

    }

    @Override
    public void unSubscribe() {

    }

    @Override
    public LoginContact.View getView() {
        return mLoginView;
    }
}

分析这个类,先从上到下来分析。

首先,这个类实现了LoginContact.Presenter。这意味着,接口Presenter规范并定义了这个抽象类的全部或部分行为。理论上来讲,这个实现了Presenter协议的类,不应该有其它公有的,非抽象的方法。如果要增加公有的,非抽象方法,应该修改协议,来实现扩展。即是,面向协议,而非面向对象。

然后,再来看这个类的成员字段。一共4个,我们只讨论其中2个LoginContact.View和LoginInterceptor:

1、LoginContact.View。这个成员字段,即是MVP里面的V的代表者。之所以,需要这个字段。是因为,当Presenter对M处理完数据后,需要通知V层更新界面,以响应用户的操作。所以,这个字段的大部分使用场景,就是被用来实现V层的界面刷新的。但不是,让你随便刷新所有UI的。你所能对UI做的事情,都被限制在LoginContact.View这个协议里面。

2、LoginInterceptor。这个成员字段,是用来检查一些常见的登录参数是否合法。比如,手机号码、密码、验证码之类的长度、输入类型是否正确。

接着,我们往下,再继续看看AbstractLoginPresenter的构造方法,里面有两个参数,重点看LoginContact.View。可以看到,Presenter是在构造的时候,与View层,通过构造方法,实现了相互绑定。当然,绑定二者的方式有多种,这只是一种。

接着,我们往下,再继续看onLoginWithPassWord(@NonNull ArrayMap

 @Override
    public void onLoginWithPassWord(@NonNull String phoneNumKey, @NonNull String phoneNumValue,
                                    @NonNull String passWordKey, @NonNull String passWordValue) {

        if ( mLoginInterceptor.checkLoginWithPassWord(mApplication,
                phoneNumValue, passWordValue)) {

            mLoginParams.put(phoneNumKey, phoneNumValue);
            mLoginParams.put(passWordKey, passWordValue);
            onLoginWithPassWord(mLoginParams);
        }

    }

这个方法,调用LoginInterceptor来检查过滤登录参数。如果参数不合法,则弹出toast提示用户。如果合法,则调用,会被子类实现的抽象方法,并传合法参数给过去,子类就省去再检查的麻烦了。

最后,剩下的三个方法。subscribe和unSubscribe比较简单,就不做解释了。getView,主要是统一用来获取V层的代理对象。

说完了抽象的AbstractLoginPresenter,是时候来说一下具体的LoginPresenter,这一步,也是实际的开发项目,需要做的。首先是创建一个LoginPresenter类,并继承自AbstractLoginPresenter,代码如下:

/**
 * @Author Lyf
 * @CreateTime 2018/2/28
 * @Description A LoginPresenter is a middle-man for View and Model. 
 **/
public class LoginPresenter extends AbstractLoginPresenter {

    public LoginPresenter(Application mApplication, LoginContact.View mLoginView) {
        super(mApplication, mLoginView);
    }

    @Override
    public void onLoginWithPassWord(@NonNull ArrayMap<String, Object> mLoginParams) {

        // Posts a login request to remote server.
        PostLoginRequest.PostLogin(mLoginParams, new Callback<BaseBean>() {
            @Override
            public void onFailure(int errorCode, String errorMsg, Response<BaseBean> response) {

            }

            @Override
            public void onResponse(BaseBean bean, Response<BaseBean> response) {

            }
        });

    }

}

这个类很简单,构造方法没什么好讲的,跟AbstractLoginPresenter里面的一样,只是负责传递而已。重点看被其重写的onLoginWithPassWord()方法。可以看到,在onLoginWithPassWord里面,做了登录的请求。但是,请注意。这里,并没有直接在onLoginWithPassWord方法里面做登录请求,而是继续,将登录请求,外包给PostLoginRequest类,这个类的作用,也就是我们在上文所说的,MVP中的M。

那么,这样做的好处是什么呢?干嘛,还要多抽出一个PostLoginRequest类,这么麻烦?是不是画蛇添足,过度设计了?并不是,因为,PostLoginRequest通过调用PostLogin()来实现登录请求,并对数据进行过滤处理,最后,将过滤后的数据,返回给LoginPresenter。这样LoginPresenter就可以不用管,数据处理的细节,一心一意的做它的中介者角色。。。

举个例子:假设一下,如果有一天,换了一个网络库或是其它的操作,导致登录请求这个动作,需要做较大的改动。那P和V层,都不会受到影响。因为,M层只管处理后,照着原定的CallBack接口定义的数据格式,返回数据就行。至于,它做了什么修改,P和V是不管的。如果你的网络框架层封装的足够好的话,这个PostLoginRequest类,甚至可以用在其它项目上,只需做很少甚至不用做改动,这是抽出M层的好处。(PostLogin里面的具体实现,就不放出来了,不增加文章篇幅,后面会给上demo的地址,可以自己去里面看)

好了,看完了Prensenter和Model,还剩下最后一个View。MVP的V在android中,通常有两种表现方式,一种是像Presenter一样,再创建一个LoginView。一种是,直接使用Activity/Fragment充当View。具体,看需求,只要达到解藕的目的就行,不要读死书。我这里是,拿Activity充当View用,Google的MVP示例项目也是,不过他的是用Fragment。废话不多说,看LoginActivity的代码:

/**
 * @Author Lyf
 * @CreateTime 2018/2/5
 * @Description A login Activity implements LoginContact.View
 **/
public class LoginActivity extends BaseActivity implements LoginContact.View {

    private LoginContact.Presenter mPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mPresenter = new LoginPresenter(getApplication(),this);
        onLoginClick();
    }

    // fakes a login action.
    private void onLoginClick() {

        // Delivery params to presenter which will check the params, 
        // and then post a login request if the params is valid.
        mPresenter.onLoginWithPassWord("phone", "132123456789",
                "password", "*****");
    }

    @Override
    public void showLoading() {
        // Shows a login dialog.
    }

    @Override
    public void hideLoading() {
        // Hides a login dialog.
    }

}

LoginActivity的代码也很简短,只有不到50行。就像看LoginPresenter一样,咱们先从LoginActivity的定义入手。首先,LoginActivity是BaseActivity的子类,这个不需要解释了。其次,LoginActivity实现了LoginContact.View接口。也就是说,LoginActivity遵循了LoginContact.View所定义的协议。

往下看,可以看到,该类只有一个成员字段:

 private LoginContact.Presenter mPresenter;

一个LoginContact.Presenter协议类型的成员字段。那么,它是怎么被初始化的呢?让我们看一下onCreate方法:

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mPresenter = new LoginPresenter(getApplication(),this);
        onLoginClick();
    }

从代码中可以看到,mPresenter字段,实际上是代理了LoginPresenter的对象。这样,当V需要对M层的数据进行处理的时候,就可以通过mPresenter来实现。下面的onLoginClick()方法,就是一个示例:

 // fakes a login action.
 private void onLoginClick() {

        // Delivery params to presenter which will check the params, 
        // and then post a login request if the params is valid.
        mPresenter.onLoginWithPassWord("phone", "132123456789",
                "password", "*****");
   }

模拟一个登录点击事件,V层被点击后,会调用mPresenter的onLoginWithPassWord方法。首先,AbstractPresenter的相应方法,会被调用来对参数的合法性进行检查。然后,再由它继续调用LoginPresenter的onLoginWithPassWord(ArrayMap)方法,最后,由M层,实现对网络的请求。


本文到此为止… Thank you

附上可以跑的代码(0 warnings, 0 errors):
https://github.com/SuperBeagleDog/OkMVP

注意:
这个库里面有很多东西,Login模块的示例代码,其中,抽象的,通用部分,都放在login这个module下。实际项目接入运用的,放在com.lyf.okmvp.ui.login包下。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值