MVP 设计模式

讲解设计模式

MVP 设计模式

原理:

MVP是有MVC演变而来的,因为在Android中用MVC来写的时候,Activity到底是View层还是Controller层真的是傻傻分不去清楚,所以基本它就把这两层的活都自己一个人干了,这样一来Activity上的代码量会非常大,Controller层和View层没有实现解耦分离开来,如果后面这两层代码其中一层出了问题你就可能两层都的修改,代码是没什么重用性可言的。而MVP的出现就是为了解决这些问题的,MVP模式将Controller的工作抽取出来交给Presenter层,Presenter层负责控制处理业务逻辑,作为中间层建立起Model层和View层的联系,从而实现三层交互。这么说确实挺难让人理解的,下面就通过一个例子,在例子中夹杂着解释让我们掌握MVP的用法吧。

属性:(我就拿登录来当作例子)
  • Model层:创建用于保存用户信息的实体类User(就是userbean)登录的时候用来保存用户信息,建议也保存到本地一份
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class User {
private String username; //用户的账号
private String password; //用户的密码

public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}

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

  • 结果回调接口:

    1. 创建一个接口,用来通知登录是否成功(成功的话,就可以把获取到的userbean传递下去)或者 失败
    2. 代码展示:
      1
      2
      3
      4
      5
      6
          public interface OnLoginListener {
      //登录成功的回调
      void loginSuccess(User user); //登录成功后,将用户信息传递进去
      //登录失败的回调
      void loginFailed();
      }
  • 定义业务接口:

    1. 这里抽出一个接口的目的是为了降低一层耦合和便于复用。比如同一个网络请求业务你可以有OkHttp的实现,同时也可以有Retrofit的实现,这样一来就可以方便的做到网络框架的替换,当然你还可以有不同内容的实现。简单来说,就是将登录的这个功能抽取出来,成为一个接口,方便复用。
    2. 代码展示:
      1
      2
      3
      4
      5
          public interface UserLogin{
      //登录方法
      public void login(String username, String password, OnLoginListener loginListener);
      //这里就传入用户输入的账号,密码,在传入一个登录状况判断的接口
      }
  • 具体Model的实现类:

    创建一个具体的类,来实现UserLogin的接口,来实现里面的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class UserGo implements UserLogin {
    @Override
    public void login(final String username, final String password, final OnLoginListener loginListener) {
    //模拟网络请求耗时操作
    new Thread() {
    @Override
    public void run() {
    //模拟了耗时
    try {
    Thread.sleep(2000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    //模拟登录成功
    if ("luoqiang".equals(username) && "108".equals(password)) {
    User user = new User();
    user.setUsername(username);
    user.setPassword(password);
    loginListener.loginSuccess(user);
    } else {
    loginListener.loginFailed();
    }
    }
    }.start();
    }
    }
  • View层:

    1. View层不关心数据,不关心逻辑处理!只关心和用户的交互,那么这个登录界面应该有的操作就是:从输入框获取用户名,获取密码,清除用户名,清除密码,登录网络请求时显示进度条,隐藏进度条,登录成功跳转到对应界面,登录失败提示。接下来定义接口如下:

      这里并没有将activity来直接当作View层,原因也是为了方便复用,那个Fragment/Activity需要用到登录,就实现这个接口就行了。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
          public interface UserLoginView {
      String getUserName();
      //获取用户输入的账号
      String getPassword();
      //获取用户输入的密码
      void clearUserName();
      //清除用户输入的账号
      void clearPassword();
      //清除用户输入的密码
      void showLoading();
      //显示加载(一般来说,我都会在BaseActivity/BaseFragment中实现Loading的显示和隐藏,这里就没必要在写)
      void hideLoading();
      //隐藏加载
      void toMainActivity(User user);
      //跳转到主页,顺便传递用户信息
      void showFaileTips();
      //登录失败的提示
      }

    2. 具体的用法:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
          public class UserLoginActivity extends AppCompatActivity implements UserLoginView {
      private EditText mEtUsername, mEtPassword;
      private Button mBtnLogin, mBtnClear;
      private ProgressBar mPbLoading;
      private UserLoginPresenter mUserLoginPresenter;

      @Override
      protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      //关于Presenter层,都可以写在BaseActivtiy/BaseFragment中,没必要写在Activity中
      mUserLoginPresenter = new UserLoginPresenter(this);
      //获取Presenter层
      initViews();
      }

      private void initViews() {
      mEtUsername = findViewById(R.id.et_username);
      mEtPassword = findViewById(R.id.et_password);
      mBtnClear = findViewById(R.id.btn_clear);
      mBtnLogin = findViewById(R.id.btn_login);
      mPbLoading = findViewById(R.id.pb_loading);
      mBtnLogin.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
      mUserLoginPresenter.login();
      }
      });
      mBtnClear.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
      mUserLoginPresenter.clear();
      }
      });
      }

      @Override
      public String getUserName() {
      return mEtUsername.getText().toString();
      }

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

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

      @Override
      public void clearPassword() {
      mEtPassword.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, "跳转到登录成功页面", Toast.LENGTH_SHORT).show();
      }

      @Override
      public void showFaileTips() {
      Toast.makeText(this, "登录失败", Toast.LENGTH_SHORT).show();
      }

      @Override
      protected void onDestroy() {
      super.onDestroy();
      //为了防止内存泄漏,解绑Presenter层对View层的引用,这里的将Presenter层置为空可以在BaseActivity/BaseFragment中进行操作
      mUserLoginPresenter.detachView();
      }
      }

  • Presenter层:

    1. Presenter的作用就是从View层获取用户的输入,传递到Model层进行处理,然后回调给View层,输出给用户!
    2. Presenter一般来说,就是将View和Mode集合在一起,进行操作
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
          public class UserLoginPresenter {
      private UserGo userGo;
      private UserLoginView userLoginView;
      private Handler mHandler = new Handler();
      //对应视图页面销毁的标志位,当视图销毁后回调就不需要处理了
      private boolean destroyFlag;

      //Presenter必须要能拿到View和Model的实现类
      public UserLoginPresenter(IUserLoginView userLoginView) {
      this.userLoginView = userLoginView;
      this. userGo = new UserGo();
      }

      public void login() {
      userLoginView.showLoading();
      userGo.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() {
      @Override
      public void loginSuccess(final User user) {
      if (!destroyFlag) { //View层销毁后不需要处理的判断
      //需要在UI线程执行
      mHandler.post(new Runnable() {
      @Override
      public void run() {
      userLoginView.toMainActivity(user);
      userLoginView.hideLoading();
      }
      });
      }
      }

      @Override
      public void loginFailed() {
      if (!destroyFlag) { //View层销毁后不需要处理的判断
      //需要在UI线程执行
      mHandler.post(new Runnable() {
      @Override
      public void run() {
      userLoginView.showFaileTips();
      userLoginView.hideLoading();
      }
      });
      }
      }
      });
      }

      public void clear() {
      userLoginView.clearUserName();
      userLoginView.clearPassword();
      }

      //解绑视图
      public void detachView() {
      destroyFlag = true;
      this.userLoginView = null;
      }
      }