[关闭]
@zhongzilu 2016-03-28T14:14:22.000000Z 字数 5761 阅读 2797

Android开发 MVP之初体验

Android MVP MVC


本文为作者原创,转载时请写上转载地址:https://www.zybuluo.com/zhongzilu/note/327459

熟悉安卓开发的童鞋应该都熟悉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文件

  1. public class LoginActivity extends Activity implements LoginView, View.OnClickListener {
  2. private ProgressBar progressBar;
  3. private EditText username;
  4. private EditText password;
  5. private LoginPresenter presenter;
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_login);
  10. //实例化控件
  11. ...
  12. presenter = new LoginPresenterImpl(this);
  13. }
  14. ...

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

现在打开LoginView.java

  1. public interface LoginView {
  2. void showProgress();
  3. void hideProgress();
  4. void setUsernameError();
  5. void setPasswordError();
  6. void navigateToHome();
  7. }

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

  1. @Override public void showProgress() {
  2. progressBar.setVisibility(View.VISIBLE);
  3. }
  4. @Override public void hideProgress() {
  5. progressBar.setVisibility(View.GONE);
  6. }
  7. @Override public void setUsernameError() {
  8. username.setError(getString(R.string.username_error));
  9. }
  10. @Override public void setPasswordError() {
  11. password.setError(getString(R.string.password_error));
  12. }
  13. @Override public void navigateToHome() {
  14. startActivity(new Intent(this, MainActivity.class));
  15. finish();
  16. }

打开LoginPresenter.java

  1. public interface LoginPresenter {
  2. void validateCredentials(String username, String password);
  3. void onDestroy();
  4. }

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

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

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

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

  1. public class LoginPresenterImpl implements LoginPresenter,
  2. LoginInteractor.OnLoginFinishedListener {
  3. private LoginView loginView;
  4. private LoginInteractor loginInteractor;
  5. public LoginPresenterImpl(LoginView loginView) {
  6. this.loginView = loginView;
  7. this.loginInteractor = new LoginInteractorImpl();
  8. }
  9. //其他方法的实现
  10. ...
  11. }

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

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

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

  1. @Override public void validateCredentials(String username, String password) {
  2. if (loginView != null) {
  3. loginView.showProgress();
  4. }
  5. loginInteractor.login(username, password, this);
  6. }

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

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

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

  1. public interface LoginInteractor {
  2. interface OnLoginFinishedListener {
  3. void onUsernameError();
  4. void onPasswordError();
  5. void onSuccess();
  6. }
  7. void login(String username, String password, OnLoginFinishedListener listener);
  8. }

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

  1. @Override public void onUsernameError() {
  2. if (loginView != null) {
  3. loginView.setUsernameError();
  4. loginView.hideProgress();
  5. }
  6. }
  7. @Override public void onPasswordError() {
  8. if (loginView != null) {
  9. loginView.setPasswordError();
  10. loginView.hideProgress();
  11. }
  12. }
  13. @Override public void onSuccess() {
  14. if (loginView != null) {
  15. loginView.navigateToHome();
  16. }
  17. }

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

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

  1. public class LoginInteractorImpl implements LoginInteractor {
  2. @Override
  3. public void login(final String username, final String password, final OnLoginFinishedListener listener) {
  4. // Mock login. I'm creating a handler to delay the answer a couple of seconds
  5. new Handler().postDelayed(new Runnable() {
  6. @Override public void run() {
  7. boolean error = false;
  8. if (TextUtils.isEmpty(username)){
  9. listener.onUsernameError();
  10. error = true;
  11. }
  12. if (TextUtils.isEmpty(password)){
  13. listener.onPasswordError();
  14. error = true;
  15. }
  16. if (!error){
  17. listener.onSuccess();
  18. }
  19. }
  20. }, 2000);
  21. }
  22. }

我们可以看到该类实现了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

流程图如下:

Created with Raphaël 2.1.2LoginActivityLoginActivityLoginPresenterImplLoginPresenterImplLoginInteractorImplLoginInteractorImplLoginPresentervalidateCredentials接口LoginInteractorlogin接口LoginInteractor.OnLoginFinishedListeneronSuccess接口LoginViewnavigateToHome接口

总结

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

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

最后

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

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注