尝试使用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; }基本上就是以上的流程,比较难的地方在于一开始对于通过整理各层担当,从而提取出各层的接口。接口提取出来之后整个逻辑框架就会变的很清晰了。