您的位置:首页 > 移动开发 > Android开发

Android:MVP模式

2016-05-24 16:12 459 查看


简介

关于MVP的介绍网上有很多,这里不再累述,虽然大家的实现方式也许各不相同,但是有一些基本的共识:
Model:数据模型;
View:用户界面,一般对应于Activity、Fragment;
Presenter:作为View和Model的桥梁,实现两者解耦。

MVP的核心是:将View中与UI无关的部分逻辑转移到Presenter中。

其依赖关系如下: 

View与Presenter相互依赖,Presenter依赖于Model,View和Model解耦。 


实例

经典的登陆例子 


Model

数据来源,一般是从网络或者数据库异步加载通过接口回调。我们从内存模拟,然后通过handler#postDelayed来模拟加载过程。
publicclassLoginModel {

publicstatic Map<String, String> maps = new HashMap<>();
private Handler mHandler = new Handler();

static {
maps.put("key1", "password1");
maps.put("key2", "password2");
maps.put("key3", "password3");
}

publicvoid getPassword(final String username, final OnGetPasswordListener listener) {
mHandler.postDelayed(new Runnable() {
@Overridepublicvoid run() {
String password = maps.get(username);
if (password == null) {
listener.onGetError();
} else {
listener.onGet(password);
}
}
}, new Random().nextInt(2000));
}

publicvoid addUser(final String username, final String password, final OnRegisterListener registerListener) {
mHandler.postDelayed(new Runnable() {
@Overridepublicvoid run() {
try {
if (LoginModel.maps.containsKey(username)) {
registerListener.onUserExist();
} else {
LoginModel.maps.put(username, password);
registerListener.onSuccessful();
}
} catch (Exception e) {
e.printStackTrace();
registerListener.onNetError();
}
}
}, new Random().nextInt(2000));
}

publicinterfaceOnGetPasswordListener {
void onGet(String password);

void onGetError();
}

publicinterfaceOnRegisterListener {
void onSuccessful();

void onUserExist();

void onNetError();
}
}


View

首先明确一个View的职责,以LoginActivity为例,他要做的是:

用户点击登陆:
检查账号密码合法性;
登陆过程给用户提示;
如果登陆成功,用户提示消失,执行其他操作;
如果登陆失败,用户提示消失,执行其他操作;

用户点击清除:清除已经输入的内容

用户点击注册:跳转到注册界面

我们将其抽象为一个接口:
publicinterface ILoginView {

void usernameIllegal();

void passwordIllegal();

void showProgress();

void dismissProgress();

void loginSuccessfully();

void loginUnsuccessfully();

void clearState();

}
17

然后View去实现上诉接口:
publicclassLoginActivityextendsAppCompatActivityimplementsILoginView {

@InjectView(R.id.et_username)
EditText mEtUsername;
@InjectView(R.id.et_password)
EditText mEtPassword;
@InjectView(R.id.btn_login)
Button mBtnLogin;
@InjectView(R.id.btn_clear)
Button mBtnClear;
private ProgressDialog mProgressDialog;

private LoginPresenter mLoginPresenter = new LoginPresenter(this);

@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
mProgressDialog = new ProgressDialog(this);
mBtnLogin.setOnClickListener(new View.OnClickListener() {
@Overridepublicvoid onClick(View v) {
mLoginPresenter.doLogin(mEtUsername.getText().toString(), mEtPassword.getText().toString());
}
});

mBtnClear.setOnClickListener(new View.OnClickListener() {
@Overridepublicvoid onClick(View v) {
mLoginPresenter.doClear();
}
});
}

@Overridepublicvoid showProgress() {
mProgressDialog.show();
}

@Overridepublicvoid dismissProgress() {
mProgressDialog.dismiss();
}

@Overridepublicvoid loginSuccessfully() {
Toast.makeText(LoginActivity.this, "login successfully", Toast.LENGTH_SHORT).show();
}

@Overridepublicvoid loginUnsuccessfully() {
Toast.makeText(LoginActivity.this, "login unsuccessfully, please check", Toast.LENGTH_SHORT).show();
}

@Overridepublicvoid clearState() {
mEtUsername.getText().clear();
mEtPassword.getText().clear();
}

@Overridepublicvoid usernameIllegal() {
mEtUsername.setError("should not be empty");
}

@Overridepublicvoid passwordIllegal() {
mEtPassword.setError("should not be empty");
}

@Overridepublicboolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
returntrue;
}

@Overridepublicboolean onOptionsItemSelected(MenuItem item) {

int id = item.getItemId();

if (id == R.id.action_settings) {
startActivity(new Intent(this, RegisterActivity.class));
returntrue;
}

returnsuper.onOptionsItemSelected(item);
}
}
91

可以看到上诉的View只执行UI相关的操作,没有任何逻辑,逻辑都转移到了Prensenter。有一些比较简单的逻辑可以不必转移到Presenter Layer,比如clearState,这一逻辑完全可以直接在View层调用而不经过Presenter回调。还有输入合法性判断,只判断其是否为空也是很简单的逻辑,同样可以在View层实现。总而言之,简单的逻辑可以在View中处理。


Presenter

最后也是最关键的,在Presenter中连接View与Model,承担View的大部分逻辑。
publicclass LoginPresenter {

private ILoginView mLoginView;

private LoginModel mModel = new LoginModel();

public LoginPresenter(ILoginView loginView) {
mLoginView = loginView;
}

publicvoid doLogin(String inputUsername, final String inputPassword) {
if (inputUsername.equals("")) {
mLoginView.usernameIllegal();
return;
}
if (inputPassword.equals("")) {
mLoginView.passwordIllegal();
return;
}
mLoginView.showProgress();
mModel.getPassword(inputUsername, new LoginModel.OnGetPasswordListener() {
publicvoid onGet(String password) {
if (inputPassword.equals(password)) {
mLoginView.loginSuccessfully();
} else {
mLoginView.loginUnsuccessfully();
}
mLoginView.dismissProgress();
}

publicvoid onGetError() {
mLoginView.loginUnsuccessfully();
mLoginView.dismissProgress();
}
});
}

publicvoid doClear() {
mLoginView.clearState();
}
}


比较

假设没有使用MVP架构,常见的做法会是:
mBtnLogin.setOnClickListener(new View.OnClickListener() {
@Overridepublicvoid onClick(View v) {
if (mEtUsername.getText().toString().equals("")) {
usernameIllegal();
return;
}
if (mEtPassword.getText().toString().equals("")) {
passwordIllegal();
return;
}
LoginModel loginModel = new LoginModel();
showProgress();
loginModel.getPassword(mEtUsername.getText().toString(), new LoginModel.OnGetPasswordListener() {
publicvoid onGet(String password) {
if (mEtPassword.getText().toString().equals(password)) {
loginSuccessfully();
} else {
loginUnsuccessfully();
}
dism
b0f9
issProgress();
}

publicvoid onGetError() {
loginUnsuccessfully();
dismissProgress();
}
});
}
});
30

这种写法,虽然一开始写起来会非常方便,但是越到后面越臃肿而难以维护。所以使用MVP架构会使得层次清晰,View与Model解耦,代码也较为整洁。 

假设现在我要测试一下逻辑是否有误,如果是上诉写法,除了run一下工程然后手动点击测试之外,仿佛无从下手。使用了MVP架构,将View职责抽象成接口后,我们写起单元测试会比较方便。

最后,很多文章会把Model和Prensenter也抽象出一个接口,目前我不知道这样做有什么好处,所以我倾向于Model和Presenter先不需要抽象接口,需要时再进行重构。

项目地址: 
https://github.com/leelit/android-mvp-pattern
参考: 
http://blog.csdn.net/vector_yi/article/details/24719873 
https://segmentfault.com/a/1190000003927200 
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0227/2503.html 
http://blog.csdn.net/lmj623565791/article/details/46596109 
https://github.com/antoniolg/androidmvp
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: