尝试使用MVP框架实现一个登陆的demo,记录一下。
首先说一下MVP框架的各层的职责。
Module层负责用户数据,如数据库的查询,网络查询等。
View层负责更新UI界面,向外部提供更新UI的接口,在Activity或者Fragment中进行实现。
Presenter层负责逻辑的操作,将Module和View层关联起来,向外提供逻辑接口和实现类,在Activity或者Fragment中可直接进行调用。
View和Presenter的实例需要相互绑定。大致的关系:View⇔Presenter←Module
下面用MVP框架实现一个用户登陆的模型。
代码结构如图:
View层提供的接口用于去更新UI控件,首先需要考虑一下都在什么情况下需要更新UI。
1.当登陆开始的时候需要显示progressbar
2.当登陆成功/失败时需要显示toast
3.当点击清除按钮时需要将EditText中的用户数据清空
上记情况整理出来之后就可以提取出下面的接口了。
该接口的实现是在Activity或者Fragment中进行实现,在Presenter中进行调用。
public interface ILoginView {
/**
* Login开始
*/
void onLoginStart();
/**
* Login成功
*/
void onLoginSuccess();
/**
* Login失败
*/
void onLoginFail();
/**
* 清除数据
*/
void onClearView();
}
Presenter提供的接口用于提供逻辑处理,逻辑处理只有两个,一个是用户点击登陆按钮;一个是用户点击清除按钮。
所以Presenter只需要向View层提供两个方法进行调用即可。
public interface ILoginPresenter {
/**
* 登录处理
* @param username
* @param password
*/
void onLogin(String username, String password);
/**
*清除处理
*/
void onClear();
}
Module层提供的接口用于提供用户数据的操作,此处只是简单提供了一个校验的方法,如果需要查询数据库或者访问网络,也需要在Module层处理。
public interface IUser {
boolean checkUserInfo(String username, String password);
}
View的实现在Activity中
@Override
public void onLoginStart() {
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
public void onLoginSuccess() {
mProgressBar.setVisibility(View.GONE);
Toast.makeText(this, R.string.login_success, Toast.LENGTH_SHORT).show();
}
@Override
public void onLoginFail() {
mProgressBar.setVisibility(View.GONE);
Toast.makeText(this, R.string.login_fail, Toast.LENGTH_SHORT).show();
}
@Override
public void onClearView() {
mUserName.setText("");
mPassword.setText("");
}
Presenter的实现
public class LoginPresenterImp implements ILoginPresenter {
private ILoginView mLoginView;
private LoginHandler mHandler;
private static final int LOGIN_SUCCESS = 1;
private static final int LOGIN_FAIL = 2;
public LoginPresenterImp(ILoginView mLoginView) {
this.mLoginView = mLoginView;
mHandler = new LoginHandler();
}
@Override
public void onLogin(final String username, final String password) {
mLoginView.onLoginStart();
new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
IUser user = new UserImp(username, password);
Message message = new Message();
if (user.checkUserInfo(username, password)) {
message.what = LOGIN_SUCCESS;
} else {
message.what = LOGIN_FAIL;
}
mHandler.sendMessage(message);
}
}.start();
}
@Override
public void onClear() {
mLoginView.onClearView();
}
class LoginHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case LOGIN_SUCCESS:
mLoginView.onLoginSuccess();
break;
case LOGIN_FAIL:
mLoginView.onLoginFail();
break;
default:
break;
}
}
}
}
还剩下最后一步,就是三者之间的相互调用
View和Presenter之间是需要相互绑定的,所以在Activity中new一个Presenter实例,并将自己绑定到Presenter中
mLoginPresenter = new LoginPresenterImp(this);
这样在Activity中可以调用Presenter实例进行逻辑处理。
public void onClick(View v) {
switch (v.getId()) {
case R.id.login:
String username = mUserName.getText().toString();
String password = mPassword.getText().toString();
mLoginPresenter.onLogin(username, password);
break;
case R.id.clear:
mLoginPresenter.onClear();
break;
default:
break;
}
}
同时把实现view接口的自己本身绑定到了Presenter中,从而在Presenter在逻辑处理完成后,可以通过调用view的接口进行UI的更新。
class LoginHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case LOGIN_SUCCESS:
mLoginView.onLoginSuccess();
break;
case LOGIN_FAIL:
mLoginView.onLoginFail();
break;
default:
break;
}
}
}
需要注意一下子线程不能直接更新UI的问题
Presenter中可以在需要时可以new User实例,调用User的方法进行用户数据的操作,本例是调用user的checkUserInfo的方法
IUser user = new UserImp(username, password);
Message message = new Message();
if (user.checkUserInfo(username, password)) {
message.what = LOGIN_SUCCESS;
} else {
message.what = LOGIN_FAIL;
}