摘要
以登錄為例子,對(duì)比使用MVP與不使用的區(qū)別。
不使用MVP,模擬登錄場(chǎng)景
public class LoginActivity extends Activity implements View.OnClickListener {
private EditText mAccountInput;
private EditText mPassowrdInput;
private Button mLoginBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// 綁定控件,設(shè)置監(jiān)聽
mAccountInput = (EditText) findViewById(R.id.input_account);
mPassowrdInput = (EditText) findViewById(R.id.input_password);
mLoginBtn = (Button) findViewById(R.id.btn_login);
mLoginBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_login:
doLogin(mAccountInput.getText().toString(), mPassowrdInput.getText().toString());
break;
default:
break;
}
}
/**
* 執(zhí)行登錄
* @param mAccount ?賬號(hào)
* @param mPassword 密碼
*/
private void doLogin(String mAccount, String mPassword) {
if (TextUtils.isEmpty(mAccount) || TextUtils.isEmpty(mPassword))
doLoginFail("賬號(hào)與密碼不能為空");
// 模擬登錄過(guò)程的異步
new AsyncTask<Void, Void, LoginInfo>() {
@Override
protected void onPreExecute() {
super.onPreExecute();
showLoading()
}
@Override
protected LoginInfo doInBackground(Void... params) {
// TODO: 網(wǎng)絡(luò)請(qǐng)求,這里直接模擬返回登錄結(jié)果
return new LoginInfo();
}
@Override
protected void onPostExecute(LoginInfo loginInfo) {
super.onPostExecute(loginInfo);
dismissLoading()
if (loginInfo.isSuccess()) {
doLoginSuccess(loginInfo);
} else {
doLoginFail(loginInfo.getErrorMessage());
}
}
}.execute();
}
/**
* 顯示等待提示
*/
private void showLoading() {
// TODO: 顯示等待提示
}
/**
* 隱藏等待提示
*/
private void dismissLoading() {
// TODO: 隱藏等待提示
}
/**
* 登錄成功
* @param mLoginInfo 登錄信息
*/
private void doLoginSuccess(LoginInfo mLoginInfo) {
// TODO: 執(zhí)行登錄成功邏輯
}
/**
* 登錄失敗
* @param errorMessage 失敗提示
*/
private void doLoginFail(String errorMessage) {
// TODO: 顯示錯(cuò)誤信息
}
}
使用MVP,模擬登錄場(chǎng)景
定義Model
public interface LoginModel {
/**
* 執(zhí)行登錄操作
* @param mAccount 賬號(hào)
* @param mPassword 密碼
* @param mLoginListener
*/
void doLogin(String mAccount, String mPassword, OnLoginListener mLoginListener);
/**
* 登錄異步回調(diào)
*/
interface OnLoginListener {
void finishLogin(LoginInfo mLoginInfo);
}
}
public class LoginModelImpl implements LoginModel {
@Override
public void doLogin(String mAccount, String mPassword, final OnLoginListener mLoginListener) {
if (TextUtils.isEmpty(mAccount) || TextUtils.isEmpty(mPassword)) {
LoginInfo mLoginInfo = new LoginInfo();
mLoginInfo.setSuccess(false);
mLoginInfo.setErrorMessage("賬號(hào)與密碼不能為空");
mLoginListener.finishLogin(mLoginInfo);
return ;
}
// 模擬登錄過(guò)程的異步
new AsyncTask<Void, Void, LoginInfo>() {
@Override
protected LoginInfo doInBackground(Void... params) {
// TODO: 網(wǎng)絡(luò)請(qǐng)求,這里直接模擬返回登錄結(jié)果
return new LoginInfo();
}
@Override
protected void onPostExecute(LoginInfo loginInfo) {
super.onPostExecute(loginInfo);
mLoginListener.finishLogin(loginInfo);
}
}.execute();
}
}
定義Presenter
public interface LoginPresenter {
/**
* 執(zhí)行登錄操作
* @param mAccount 賬號(hào)
* @param mPassword 密碼
*/
void doLogin(String mAccount, String mPassword);
}
public class LoginPresenterImpl implements LoginPresenter {
private LoginView mLoginView;
private LoginModel mLoginModel;
public LoginPresenterImpl(LoginView mLoginView, LoginModel mLoginModel) {
this.mLoginView = mLoginView;
this.mLoginModel = mLoginModel;
}
@Override
public void doLogin(String mAccount, String mPassword) {
mLoginView.showLoading();
mLoginModel.doLogin(mAccount, mPassword, new LoginModel.OnLoginListener() {
@Override
public void finishLogin(LoginInfo mLoginInfo) {
mLoginView.dismissLoading();
if (mLoginInfo.isSuccess()) {
mLoginView.loginSuccess(mLoginInfo);
} else {
mLoginView.loginFail(mLoginInfo.getErrorMessage());
}
}
});
}
}
定義View
public interface LoginView {
/**
* 顯示等待提示
*/
void showLoading();
/**
* 隱藏等待提示
*/
void dismissLoading();
/**
* 登錄成功
* @param mLoginInfo 登錄信息
*/
void loginSuccess(LoginInfo mLoginInfo);
/**
* 登錄失敗
* @param errorMessage 失敗提示
*/
void loginFail(String errorMessage);
}
public class LoginActivity extends Activity implements View.OnClickListener, LoginView {
private EditText mAccountInput;
private EditText mPassowrdInput;
private Button mLoginBtn;
private LoginPresenter mLoginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mLoginPresenter = new LoginPresenterImpl(this, new LoginModelImpl());
// 綁定控件,設(shè)置監(jiān)聽
mAccountInput = (EditText) findViewById(R.id.input_account);
mPassowrdInput = (EditText) findViewById(R.id.input_password);
mLoginBtn = (Button) findViewById(R.id.btn_login);
mLoginBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_login:
String mAccount = mAccountInput.getText().toString();
String mPassword = mPassowrdInput.getText().toString();
mLoginPresenter.doLogin(mAccount, mPassword);
break;
default:
break;
}
}
@Override
public void showLoading() {
// TODO: 顯示等待提示
}
@Override
public void dismissLoading() {
// TODO: 隱藏等待提示
}
@Override
public void loginSuccess(LoginInfo mLoginInfo) {
// TODO: 執(zhí)行登錄成功邏輯
}
@Override
public void loginFail(String errorMessage) {
// TODO: 顯示錯(cuò)誤信息
}
}
個(gè)人理解
- MVP會(huì)增加代碼量,是事實(shí)。
- MVP能減輕Activity/Fragment的臃腫程度。
- 當(dāng)Activity/Fragment存在更多的邏輯,雖然增加代碼量,但開發(fā)效率更高。
- 架構(gòu)不是編碼規(guī)范,不是組織代碼規(guī)范,是一種思維方式。
- 思維清晰,打代碼自然一馬平川。
- MVP易于單元測(cè)試。
無(wú)窮的思考
- 一個(gè)Activity對(duì)應(yīng)一個(gè)Fragment,由Fragment負(fù)責(zé)View的職責(zé)。
- Activity僅僅管理生命周期,管理MVP三者的實(shí)例化和三者的引用關(guān)系。
- Activity是上帝。
- 這樣更加貼合MVP的?概念。
最后
要概念上的合理,還是現(xiàn)實(shí)中的方便,其實(shí)都可以,見仁見智。