由于公司里的架构模式用到MVP,觉得自己还不够熟悉,决定在此理一理,并给大家一起总结下。

一 MVP模式介绍

MVP全称Model View Presenter。

MVP能够有效的降低View的复杂性,避免业务逻辑被塞进View中,防止View的代码变得冗杂。MVP模式会解除View与Model的耦合,同时又带来了良好的扩展性、可测试性,保证了系统的整洁性、灵活性。

肯能对于简单的应用来说MVP稍显麻烦,各种各样接口与概念,使得整个应用充斥着零散的接口,但是对于比较复杂的应用,MVP模式是一种良好的架构模式,它能够非常好的组织应用结构。

mvp 意思 android 架构 mvp架构模式_架构


MVP模式可以分离显示层和逻辑层,它们之间通过接口进行通信,降低耦合。理想化的MVP可以实现同一份逻辑代码搭配不同的显示界面,因为它们之间并不依赖与具体,而是依赖与抽象。

其实这边讲View层和Model层分离,也是相比MVC模式有一个很大的改进,我们知道,在Android当中,我们对View的更改需要通过异步来出来,MVP模式切除了View与Model的联系,通过异步的方式来将二者联系在一起,这个分离做的很好。

对于一个可扩展、稳定的应用来说,我们需要定义分离各个层,主要是UI层、业务逻辑层和数据层。毕竟,我们不知道以后还要加入什么逻辑,是从本地数据库检索数据?还是从远程的服务器中?我们的UI、数据库是否会被替换?例如,随着产品的升级,我们的UI可能会被重新设计,若UI发生了变化,此时由于业务逻辑耦合在View中,UI变化导致我们修改新的View控件,此时你就需要到原来的View中抽离具体的业务逻辑,这将是一件令人非常折磨人也易于出错的事,到最终你还是要将业务逻辑抽离开来。

MVP并不是一个标准化的模式,它有很多种实现方式,我们也可以根据自己的需求和自己认为对的方式去修正MVP的实现方式,它可以随着Presenter的复杂层度变化。只要保证我们是通过Presenter将View和Model解耦合、降低类型复杂度、各个模块可以独立测试、独立变化,这就是正确的方向。

二、 MVP模式的三个角色

1.Presenter—–交互中间人
Presenter主要作为沟通View和Model的桥梁,它从Model层检索数据后,返回给View层,使得View和Model之间没有耦合,也将业务逻辑从View角色上抽离出来。
2.View—–用户界面
View通常是指Activity、Fragment或者某个View控件,它含有一个Presenter成员变量。通常View需要实现一个逻辑接口,将View上的操作转交给Presenter进行实现,最后,Presenter调用View逻辑接口将结果返回给View元素。
3.Model—–数据的存取
对于一个结构化的APP来说,Model角色主要是提供数据的存取功能。Presenter需要通过Model层存储、获取数据,Model就像一个数据仓库。更直白的说,Model是封装了数据库DAO或者网络获取数据的角色,或者两种数据获取方式的集合。

三、MVP实战举例

讲了那么多概念,这里我们以一个简单的登陆业务逻辑来应用我们的MVP模式。

3.1

首先,我们来看一下整体结构

mvp 意思 android 架构 mvp架构模式_mvp_02

bean层代表的是model层
presenter层代表是处理数据和View层的交互的
view层就是视图层
IUserBiz是一个接口,UserBiz是这个接口的实现类

3.2
首先看下model层
这个类比较简单,就是定义一些变量和方法

public class User {
    private String usename;
    private String password;

    public String getUsename() {
        return usename;
    }
    public void setUsename(String usename) {
        this.usename = usename;
    }
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

接着看下IUserbiz,以及它的实现类IUserBiz,模拟登录操作,等登录操作做完,有一个回调接口来通知登录状态完成

public interface IUserBiz {
    public void login(String username,String password,OnLoginListener loginListener);
}
public class UserBiz implements IUserBiz {
    @Override
    public void login(final String username, final String password, final OnLoginListener loginListener) {
        //模拟子线程耗时操作
       Thread thread=new Thread(new Runnable() {
           @Override
           public void run() {
               try{
                   Thread.sleep(2000);
               }catch (InterruptedException e){
                   e.printStackTrace();
               }
               //模拟登陆成功
               if("aaa".equals(username)&&"123".equals(password)){
                   User user=new User();
                   user.setUsename(username);
                   user.setPassword(password);
                   //回调接口
                   loginListener.loginsuccess(user);
               }else {
                   loginListener.loginFailed();
               }
           }
       });
       thread.start();
    }
}

接着看下View层,里面有很多操作的方法可以去重写,根据自己的经验和严谨的思维去设计这个接口

public interface IUserLoginView {
    String getUsername();
    String getPassword();
    void clearUserName();
    void clearPassword();
    void showLoading();
    void hideLoading();
    void toMainActivity(User user);
    void showFailedError();
}

我们继续看下这个的是实现类,也就是我们的activity,在这里实现了我们刚刚定义的方法因为我们用MVP的设计模式,使用的是我们的接口,使我们的代码看上去非常清晰

public class UserLoginActivity extends AppCompatActivity implements IUserLoginView {
...........
 @Override
    public String getUsername() {
        return etUsername.getText().toString();
    }

    @Override
    public String getPassword() {
        return etPassword.getText().toString();
    }

    @Override
    public void clearUserName() {
          etUsername.setText("");
    }

    @Override
    public void clearPassword() {
          etPassword.setText("");
    }

    @Override
    public void showLoading() {
        mPbLoading.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoading() {
       mPbLoading.setVisibility(View.GONE);
    }

    @Override
    public void toMainActivity(User user) {
        Toast.makeText(this,user.getUsename()+"login success,to MainActivity",Toast.LENGTH_LONG).show();
    }

    @Override
    public void showFailedError() {
      Toast.makeText(this,"login failed",Toast.LENGTH_LONG).show();
    }
}
.....
}

最后,看下我们的Presenter,代表的就是数据和view交互的桥梁,方法比较简单,里面是具体的交互操作,逻辑非常清楚。

public class UserLoginPresenter {
...
   public void login(){
        userLoginView.showLoading();
        //实现接口
        userBiz.login(userLoginView.getUsername(), userLoginView.getPassword(), new OnLoginListener() {
            @Override
            public void loginsuccess(final User user) {
                //需要在UI线程执行
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                   userLoginView.toMainActivity(user);
                   userLoginView.hideLoading();
                    }
                });
            }

            @Override
            public void loginFailed() {
                //需要在UI线程执行
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    userLoginView.showFailedError();
                    userLoginView.hideLoading();
                }
            });
            }
        });
    }
    }

源码连接:
demo传送门