@zhongzilu
2016-03-28T14:14:22.000000Z
字数 5761
阅读 3280
Android MVP MVC
本文为作者原创,转载时请写上转载地址:https://www.zybuluo.com/zhongzilu/note/327459
熟悉安卓开发的童鞋应该都熟悉MVC架构模式, MVC开发模式即:
Model(模型) -- View(视图) -- Control(控制器)
逻辑图如下:

而这种开发模式有个缺点,就是在项目越来越大时,文件会越来越多,查找起来很不方便,而且Activity或者Fragment里面的代码会越来越臃肿,看起来很凌乱,要修改更是让人头疼,于是就提出了一种MVP的开发模式,MVP开发模式即:
Model(模型) -- View(视图) -- Presente(任命者/中介)
逻辑图如下:

网上有很多讲解MVP的文章和教程,我这里就不详细介绍了,但还有很多人看过教程之后依然不知如何实现MVP,今天主要通过实际代码的方式来体验MVP开发
本次使用的代码为Github上的一个开源项目,名为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;@Overrideprotected 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 {@Overridepublic 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 secondsnew 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接口
好了,整个登录流程讲完了,最后我们再来梳理一下整个流程:
- 在LoginActivity中,通过LoginPresenter接口类中的validateCredentials接口,把username和password传递给LoginPresenter的实现类LoginPresenterImpl
- LoginPresenterImpl类再通过LoginInteractor接口类中的login接口, 把username和password传递给LoginInteractor的实现类LoginInteractorImpl
- 在LoginInteractorImpl类中进行具体的逻辑处理, 如果验证通过,则调用LoginInteractor.OnLoginFinishedListener接口类中的onSuccess接口, 即调用了LoginPresenterImpl类中实现的onSuccess接口
- LoginPresenterImpl类中实现的onSuccess接口中,调用了LoginView接口类中的navigateToHome接口,即调用了LoginActivity类中实现的navigateToHome接口
- 最后在navigateToHome接口中实现Activity的跳转,跳转到MainActivity
流程图如下:
在MVP开发模式中, Activity只负责界面的显示和跳转相关的操作, 具体的逻辑处理通过调用接口来传递给Presenter(中介者) 既不做界面显示的操作,也不做具体的逻辑处理操作, 具体的逻辑处理通过调用接口,让Interactor即交互类来完成
交互类的逻辑处理结果,通过调用Presenter中实现的接口, 再由Presenter要实现的接口中调用Activity中实现的接口来进行UI的显示或界面跳转
好了,安卓开发 MVP之初体验讲完了,写了那么多,希望大家能够看懂了,如果还有什么问题,可以问我Follow Me