目前在软件行业有很多小公司在项目开发规划期由于没有做很好的整体规划、需求也没有很明确也考虑开发人员的架构能力不足、项目开发赶时间或其他原因就急急忙忙地着手进行代码工作,这就给以后的开发维护带来了很多问题,新人员进入项目学习成本高,单个类代码量大不易读懂,需要深入了解业务细看代码才能再进一步开发维护。
很遗憾我们公司也出现了这些问题,单个类代码量达到一千七八百行,代码耦合性非常高,View层显示、业务逻辑处理、网络数据请求代码全部都堆放在Activity里面,使得Activity变得非常的臃肿,所以近期就考虑进行代码重构,将显示层、逻辑层、数据处理层彻底分离开来,使代码的易读性、维护性都得到相应的提高。也因此了解到了MVP架构设计模式在Android上的应用。
欢迎大家关注我的个人微信公众号AndroidSharer,分享软件开发相关技术包括Android、Java Web、HTML5以及产品研发干货
什么是MVP架构设计模式?
其实MVP架构设计模式是从早期在软件行业使用广泛的MVC架构模式演变升级过来的,与MVC具有一定的相似性。MVP架构分为View层、Presenter层、Model层:
View层负责显示UI界面与用户进行交互以及结果反馈,Presenter层负责业务逻辑处理,也是View层和Model层的中间纽带它们的通信都是通过Presenter来处理的,Model层负责提供数据以及进行一定的数据处理。
从此图可以看出View层并不能直接和Model层直接交互,而是通过Presenter来进行交互,View层与用户进行交互将用户的意愿传递给Presenter处理,Presenter在进行一些逻辑处理或数据验证后向Model提出数据请求,当Model获取到数据后将数据提供给Presenter,此步操作是通过Presenter实现Model的Listener接口来实现交互的,Presenter接收到数据后在进行相应的业务逻辑处理,然后将相应的结果反馈给View层呈现给用户。
MVP架构模式的优缺点:
优点:
1、更有效的将模型与视图、逻辑处理代码分离,提供代码的可读性,降低代码耦合,使得修改视图不影响其他层代码;
2、提高Model代码复用性,所以逻辑处理都分离给Presenter非常方便单元测试;
3、提高代码的维护性;
缺点:
1、代码有冗余,类的数量会成倍增加(小项目不建议使用这种模式);
2、提高了学习成本,项目新成员需要一定的时间学习;
下面我们通过一个简单的登录功能来看看如何实现MVP:
结构图:
创建LoginView:
public interface LoginView {
String getLoginName();
String getPassword();
void showProgress();
void validate(String type);
void setDataList(List<String> dataList);
void hideProgress();
void navigateToHome();
void userNameError();
void passwordError();
void httpError();
}
创建Model获取网络请求数据,通过OnLoginFinishedListener接口与LoginPresenter进行交互:
public class LoginModelImpl implements LoginModel {
@Override
public void getUserData(String loginName, String password, final OnLoginFinishedListener listener, final Context context) {
RequestParams params = new RequestParams("这里是登录接口");
params.addQueryStringParameter("loginName",loginName);
params.addQueryStringParameter("password",password);
x.http().post(params, new Callback.CommonCallback<String>() {
@Override
public void onSuccess(String result) {
Log.e("beok",result);
JSONObject obj = null;
try {
obj = new JSONObject(result.toString());
String optResult = obj.getString("optResult");
if (optResult.equals("1")) {
//登录成功,保存相关数据
SharedPreferences sp = context.getSharedPreferences("beok_share_data", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("userName","beok");
editor.commit();
listener.setData(new ArrayList<String>());
listener.onSuccess();
} else if (optResult.equals("-1")) {
listener.onPasswordError();
} else if (optResult.equals("-2")) {
listener.onUsernameError();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
listener.onHttpError();
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
});
}
public interface OnLoginFinishedListener{
void onUsernameError();
void onPasswordError();
void onSuccess();
void onHttpError();
void setData(List<String> dataList);
}
}
并在Model里创建数据请求成功后回调接口OnLoginFinishedListener提供给Presenter实现:
创建LoginPresenter处理逻辑,通过创建LoginModelImpl对象调用其方法获取数据或进行网络请求,通过传递过来的LoginView调用相应的View显示方法呈现业务逻辑处理后的反馈结果:
/** * Created by lankong on 2016/3/14. * 逻辑的处理 */
public class LoginPresenterImpl implements LoginPresenter,LoginModelImpl.OnLoginFinishedListener {
private LoginView mLoginView ;
private LoginModel mLoginModel;
private Context mContext;
public LoginPresenterImpl(LoginView view,Context context) {
this.mContext = context;
mLoginView = view;
mLoginModel = new LoginModelImpl();
}
@Override
public void login(String loginName,String password){
if(mLoginView!=null){
if(loginName==null||loginName.equals("")){
mLoginView.validate("loginName");
return;
}
if(password==null||password.equals("")){
mLoginView.validate("password");
return;
}
mLoginView.showProgress();
}
mLoginModel.getUserData(loginName,password,this,mContext);
}
@Override
public void onUsernameError() {
if(mLoginView!=null){
mLoginView.userNameError();
}
}
@Override
public void onPasswordError() {
if(mLoginView!=null){
mLoginView.passwordError();
}
}
@Override
public void onSuccess() {
if(mLoginView!=null){
mLoginView.navigateToHome();
}
}
@Override
public void onHttpError() {
if(mLoginView!=null){
mLoginView.httpError();
}
}
@Override
public void setData(List<String> dataList) {
if(mLoginView!=null){
mLoginView.setDataList(dataList);
}
}
}
并实现OnLoginFinishedListener接口:
View 层 LoginActivity实现LoginView接口相关方法
/** * Created by lankong on 2016/3/14. * view层负责显示 */
public class LoginActivity extends AppCompatActivity implements LoginView, View.OnClickListener{
private EditText et_loginName,et_password;
private Button bt_login;
private LoginPresenterImpl mLoginPresenterImpl;
private ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
et_loginName = (EditText) findViewById(R.id.et_login);
et_password = (EditText) findViewById(R.id.et_password);
bt_login = (Button)findViewById(R.id.bt_login);
bt_login.setOnClickListener(this);
progressBar = (ProgressBar) findViewById(R.id.progress);
mLoginPresenterImpl = new LoginPresenterImpl(this,this);
}
@Override
public String getLoginName() {
return et_loginName.getText().toString();
}
@Override
public String getPassword() {
return et_password.getText().toString();
}
@Override
public void showProgress() {
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void validate(String type) {
if(type.equals("loginName")){
Toast.makeText(LoginActivity.this,"登录名不能为空",Toast.LENGTH_SHORT).show();
}else if(type.equals("password")){
Toast.makeText(LoginActivity.this,"请输入密码",Toast.LENGTH_SHORT).show();
}
}
@Override
public void setDataList(List<String> dataList) {
}
@Override
public void hideProgress() {
progressBar.setVisibility(View.GONE);
}
@Override
public void navigateToHome() {
Toast.makeText(LoginActivity.this,"登录成功",Toast.LENGTH_SHORT).show();
startActivity(new Intent(this, MainActivity.class));
finish();
}
@Override
public void userNameError() {
hideProgress();
Toast.makeText(LoginActivity.this,"用户名错误",Toast.LENGTH_SHORT).show();
}
@Override
public void passwordError() {
hideProgress();
Toast.makeText(LoginActivity.this,"密码错误",Toast.LENGTH_SHORT).show();
}
@Override
public void httpError() {
hideProgress();
Toast.makeText(LoginActivity.this,"网络异常",Toast.LENGTH_SHORT).show();
}
@Override
public void onClick(View v) {
mLoginPresenterImpl.login(getLoginName(),getPassword());
}
}
在LoginActivity中创建LoginPresenter对象,并把当前LoginView和Context传递给LoginPresenter进行一些回调显示操作。
至此MVP架构模式在Android上的实现已经完成,大家可以很明显地看到显示层、逻辑处理层、数据层完全分离,View层的修改并不会影响到Model层的代码,两者互不干预,通信通过Presenter来完成,代码结构非常清晰,维护起来非常方便,但也看到类的数量增加了,所以小项目并不合适使用这种模式。