目录

  • 概述
  • 实例
  • Model
  • View
  • Presenter
  • 后记
  • 全部代码
  • Model
  • View
  • Presenter


概述

MVP模式是Android常见的的一种架构模式,全称是Model、View、Presenter。其中,Model负责数据处理部分,View负责UI界面展示及用户操作交互,而Presenter则是负责大部分运行逻辑的编写。在Android中,常见的MVC架构模式一般是直接在Activity中实现运行逻辑,这样如果功能比较复杂,Activity的代码可读性将会很低,同时耦合性将会很高。与MVC模式相比,MVP模式断开了Model与View的直接交互,降低了代码的耦合性,也提高了代码的可复用性。

实例

本文以登录注册功能的编写演示MVP架构的实现方式。
Android中Activity一般充当View的角色,在Activity中实现对用户输入的检测以及交互;Presenter则负责将View传来的用户输入信息(UsernamePassword等)传给Model,由Model进行具体的数据判断,并将判断结果(用户是否存在、密码是否正确等)回传给Presenter,Presenter在收到Model的回传后,再调用View中的UI更新方法对界面进行更新,具体实现如下:

Model

我们编写一个LoginModelInterface接口用于表征Model,并新建一个LoginModel类实现LoginModelInterface接口。在LoginModelInterface中增加一个isUserValid(String username, String password)方法,并在LoginModel中对该方法进行实现:

public class LoginModel implements LoginModelInterface {
    @Override
    public boolean isUserValid(String username, String password) {
        // 在本方法中对用户输入的用户名及密码进行判断,并返回登录结果
        return false;
    }
}

View

我们使用MainActivity充当View的角色。同样的,我们新建一个LoginViewInterface接口,在LoginViewInterface接口中编写两个方法:loginButtonClicked()以及showLoginResult()

public interface LoginViewInterface {
    void loginButtonClicked(String username, String password);
    void showLoginResult(boolean result);
}

MainActivity实现该接口,并实现上述两个方法:

@Override
public void loginButtonClicked(String username, String password) {
    mLoginPresenter.loginButtonClicked(username, password);
}

@Override
public void showLoginResult(boolean isValid) {
    if(isValid) {
        Toast.makeText(getApplicationContext(), "Login Success !", Toast.LENGTH_LONG).show();
    } else {
        Toast.makeText(getApplicationContext(), "Login Failed !", Toast.LENGTH_LONG).show();
    }
}

loginButtonClicked()方法中调用LoginPresenter的对应方法进行具体处理;而在showLoginResult()中根据LoginPresenter回传的结果设置具体的展示。为了方便演示,我们通过Toast的方式提示登录结果。

Presenter

同样的,新建一个LoginPresenterInterface接口用于表征Presenter,并新建一个LoginPresenter类实现上述接口,重写该接口中的loginButtonClicked()方法:

@Override
public void loginButtonClicked(String username, String password) {
    boolean isValid = mLoginModel.isUserValid(username, password);
    mLoginView.showLoginResult(isValid);
}

在该方法中,通过调用LoginModelisUserValid()方法进行用户输入的合法性判断,并调用LoginViewshowLoginResult()方法对UI界面进行更新,以此实现“中间人”的角色。

后记

  • 为什么每个类都要编写一个对应的接口?
    为了提高代码的可复用性。以LoginView为例,在LoginPresenter中,为了实现对LoginView中界面更新方法的调用,在LoginPresenter的构造方法中传入了LoginView的引用。如果不定义一个LoginViewInterface而是直接传入MainActivity,那么这个LoginPresenter只能与MainActivity一一对应,而如果传入的参数设置为LoginViewInterfce,那么只要让其他的View实现LoginViewInterface,这些View就能使用LoginPresenter,提高了代码的可复用性。
  • 目前只是初步实现了MVP架构,对传入引用可能导致的内存泄漏没有过多的关注,需要大家注意,后续有时间的话笔者也会针对这一块进行补充和完善。

全部代码

Model

  • LoginModelInterface
public interface LoginModelInterface {
    boolean isUserValid(String username, String password);
}
  • LoginModel
public class LoginModel implements LoginModelInterface {
    @Override
    public boolean isUserValid(String username, String password) {
        // 在本方法中对用户输入的用户名及密码进行判断,并返回登录结果
        return false;
    }
}

View

  • LoginViewInterface
public interface LoginViewInterface {
    void loginButtonClicked(String username, String password);
    void showLoginResult(boolean isValid);
}
  • MainActivity
public class MainActivity extends AppCompatActivity implements LoginViewInterface {

    private Button mBtLogin;
    private EditText mEtUsername, mEtPassword;
    private LoginPresenterInterface mLoginPresenter;
    String TAG = "LoginPage";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
        // set click listener
        mBtLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //gain the content of usename and password,for further use
                String username = mEtUsername.getText().toString();
                String password = mEtPassword.getText().toString();
                //Tell Presenter that login button clicked
                loginButtonClicked(username, password);
            }
        });
    }

    @Override
    public void loginButtonClicked(String username, String password) {
        mLoginPresenter.loginButtonClicked(username, password);
    }

    @Override
    public void showLoginResult(boolean isValid) {
        if(isValid) {
            Toast.makeText(getApplicationContext(), "Login Success !", Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(getApplicationContext(), "Login Failed !", Toast.LENGTH_LONG).show();
        }
    }

    void init() {
        mBtLogin = findViewById(R.id.btn_login);
        mEtUsername = findViewById(R.id.et_username);
        mEtPassword = findViewById(R.id.et_password);
        mLoginPresenter = new LoginPresenter(this);
    }

}

Presenter

  • LoginPresenterInterface
public interface LoginPresenterInterface {
    void loginButtonClicked(String username, String password);
}
  • LoginPresenter
public class LoginPresenter implements LoginPresenterInterface {
    private LoginViewInterface mLoginView;
    private LoginModelInterface mLoginModel;
    public LoginPresenter(LoginViewInterface loginView) {
        mLoginView = loginView;
        mLoginModel = new LoginModel();
    }

    @Override
    public void loginButtonClicked(String username, String password) {
        boolean isValid = mLoginModel.isUserValid(username, password);
        mLoginView.showLoginResult(isValid);
    }
}