Android开发之MVP初体验

Android开发 MVP之初体验

标签 :Android MVP MVC


熟悉安卓开发的童鞋应该都熟悉MVC架构模式, MVC开发模式即:

Model(模型) – View(视图) – Control(控制器)

逻辑图如下:
MVC

而这种开发模式有个缺点,就是在项目越来越大时,文件会越来越多,查找起来很不方便,而且Activity或者Fragment里面的代码会越来越臃肿,看起来很凌乱,要修改更是让人头疼,于是就提出了一种MVP的开发模式,MVP开发模式即:

Model(模型) – View(视图) – Presente(任命者/中介)

逻辑图如下:
MVP

网上有很多讲解MVP的文章和教程,我这里就不详细介绍了,但还有很多人看过教程之后依然不知如何实现MVP,今天主要通过实际代码的方式来体验MVP开发

本次使用的代码为Github上的一个开源项目,名为androidmvp

传送门 https://github.com/antoniolg/androidmvp

先看看界面

界面很简单,就是一个登录界面和一个ListView填充的主界面,登录之后跳转到主界面,主界面上显示一个列表
登录界面主界面

第一步

用Android Studio打开项目之后可以看到如下的项目结构
项目结构

通过截图可以看出,该项目是根据界面来分类的,Login文件夹下放的是所有与登录界面有关的文件,main文件夹下放的是所有与主界面有关的文件

其中, Login文件夹下共有6个文件, 3个类文件, 3个接口文件

类文件:

  • LoginActivity
  • LoginInteratorImpl
  • LoginPresenterImpl

接口文件:

  • LoginView
  • LoginInteractor
  • LoginPresenter

第二步

打开LoginActivity.java文件

public class LoginActivity extends Activity implements LoginView, View.OnClickListener {

    private ProgressBar progressBar;
    private EditText username;
    private EditText password;
    private LoginPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        //实例化控件
        ...

        presenter = new LoginPresenterImpl(this);
    }
    ...

从上面代码可以看出,该Activity实现了LoginView的接口和View的点击事件, 并且实例化了一个LoginPresenterImpl对象

现在打开LoginView.java

public interface LoginView {
    void showProgress();

    void hideProgress();

    void setUsernameError();

    void setPasswordError();

    void navigateToHome();
}

可以看到LoginView文件中定义了几个接口, 通过名称可以很容易知道每个接口是用来干什么的
返回LoginActivity也很容易发现是如何实现这几个接口的,如下:

Override public void showProgress() {
    progressBar.setVisibility(View.VISIBLE);
}

Override public void hideProgress() {
    progressBar.setVisibility(View.GONE);
}

Override public void setUsernameError() {
    username.setError(getString(R.string.username_error));
}

Override public void setPasswordError() {
    password.setError(getString(R.string.password_error));
}

@Override public void navigateToHome() {
    startActivity(new Intent(this, MainActivity.class));
    finish();
}

打开LoginPresenter.java

public interface LoginPresenter {
    void validateCredentials(String username, String password);

    void onDestroy();
}

可以看到定义了两个接口,其中validateCredentials接口是用来验证用户名和密码是否通过验证的
在LoginActivity中也有实现

@Override public void onClick(View v) {
    presenter.validateCredentials(username.getText().toString(), 
        password.getText().toString());
}

看到这里, 我们可以总结出LoginActivity没有做任何逻辑相关的事情,而是在做界面显示相关和界面跳转的事情, 就连验证帐号的逻辑都是交给LoginPresenterImpl类的

那么现在顺藤摸瓜,打开LoginPresenterImpl.java文件

public class LoginPresenterImpl implements LoginPresenter, 
        LoginInteractor.OnLoginFinishedListener {

    private LoginView loginView;
    private LoginInteractor loginInteractor;

    public LoginPresenterImpl(LoginView loginView) {
        this.loginView = loginView;
        this.loginInteractor = new LoginInteractorImpl();
    }

    //其他方法的实现
    ...
}

我们可以看到该类实现了LoginPresenter和LoginInteractor.OnLoginFinishedListener的接口, 并且在构造方法的参数为LoginView, 那么问题来了, 为什么要传递LoginView类型的参数呢?

我们刚才也看到了,在LoginView中定义了几个接口, 而在LoginActivity中有对这几个接口的实现, 所以传递这个参数, 是有利于LoginPresenterImpl类调用LoginActivity的实现方法, 用于界面上的显示, 这就达到了逻辑代码和显示代码的分离

好了,现在来看看是怎么实现LoginPresenter中的validateCredentials接口的吧

@Override public void validateCredentials(String username, String password) {

    if (loginView != null) {
        loginView.showProgress();
    }

    loginInteractor.login(username, password, this);
}

看到这里,有同学该有疑问了,为什么在这个方法里我没看到实现帐号验证的具体逻辑呢?

没错,这里不应该有帐号验证的具体逻辑代码,因为LoginPresenterImpl类充当的是任命者或者称中介者的身份, 中介者只负责联系界面类具体实现逻辑代码的类,如果还不能理解,就想想房地产中介吧, 他们只负责联系买房者和卖房者, 其他的事都不管(抱歉, 我没有鄙视或看不起房地产中介人员的意思, 这里只是做个举例, 在此说声对不起).

我们看到代码中, 把接收到的username和password参数传递给了一个login方法, 该方法在LoginInteractor文件中, 我们再打开LoginInteractor.java文件

public interface LoginInteractor {

    interface OnLoginFinishedListener {
        void onUsernameError();

        void onPasswordError();

        void onSuccess();
    }

    void login(String username, String password, OnLoginFinishedListener listener);

}

可以定义了一个login接口和一个OnLoginFinishedListener接口类, 返回LoginPresenterImpl.java文件可以很容易看到OnLoginFinishedListener接口类中接口的实现

@Override public void onUsernameError() {
    if (loginView != null) {
        loginView.setUsernameError();
        loginView.hideProgress();
    }
}

@Override public void onPasswordError() {
    if (loginView != null) {
        loginView.setPasswordError();
        loginView.hideProgress();
    }
}

@Override public void onSuccess() {
    if (loginView != null) {
        loginView.navigateToHome();
    }
}

在这些接口的实现中同样只是调用LoginView中的接口,并没有具体的逻辑操作代码

在该类的构造方法中我们会发现, 这里实例化了一个LoginInteractorImpl对象, 我们现在打开LoginInteractorImpl.java文件

public class LoginInteractorImpl implements LoginInteractor {

    @Override
    public void login(final String username, final String password, final OnLoginFinishedListener listener) {

        // Mock login. I'm creating a handler to delay the answer a couple of seconds
        new Handler().postDelayed(new Runnable() {

            @Override public void run() {

                boolean error = false;
                if (TextUtils.isEmpty(username)){
                    listener.onUsernameError();
                    error = true;
                }
                if (TextUtils.isEmpty(password)){
                    listener.onPasswordError();
                    error = true;
                }
                if (!error){
                    listener.onSuccess();
                }
            }
        }, 2000);
    }
}

我们可以看到该类实现了LoginInteractor中的login接口, 并且在login方法中进行了具体的逻辑处理, 如果验证通过, 调用onSuccess接口

好了,整个登录流程讲完了,最后我们再来梳理一下整个流程:

  1. 在LoginActivity中,通过LoginPresenter接口类中的validateCredentials接口,把username和password传递给LoginPresenter的实现类LoginPresenterImpl
  2. LoginPresenterImpl类再通过LoginInteractor接口类中的login接口, 把username和password传递给LoginInteractor的实现类LoginInteractorImpl
  3. 在LoginInteractorImpl类中进行具体的逻辑处理, 如果验证通过,则调用LoginInteractor.OnLoginFinishedListener接口类中的onSuccess接口, 即调用了LoginPresenterImpl类中实现的onSuccess接口
  4. LoginPresenterImpl类中实现的onSuccess接口中,调用了LoginView接口类中的navigateToHome接口,即调用了LoginActivity类中实现的navigateToHome接口
  5. 最后在navigateToHome接口中实现Activity的跳转,跳转到MainActivity

流程图如下:
Alt text

总结

在MVP开发模式中, Activity只负责界面的显示和跳转相关的操作, 具体的逻辑处理通过调用接口来传递给Presenter(中介者) 既不做界面显示的操作,也不做具体的逻辑处理操作, 具体的逻辑处理通过调用接口,让Interactor即交互类来完成

交互类的逻辑处理结果,通过调用Presenter中实现的接口, 再由Presenter要实现的接口中调用Activity中实现的接口来进行UI的显示或界面跳转

最后

好了,安卓开发 MVP之初体验讲完了,写了那么多,希望大家能够看懂了,如果还有什么问题,可以问我Follow Me

本文总阅读量