@zhongzilu
2016-03-28T22:14:22.000000Z
字数 5761
阅读 2942
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;
@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接口
好了,整个登录流程讲完了,最后我们再来梳理一下整个流程:
- 在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