一直以来,想分享MVP的实战,因为很多项目开始并不是就是mvp架构的,可能是从传统的mvc结构变迁过来的。今天呈详给大家分享的这篇从mvc重构到mvp,让大家既能看到前后的对比,又能突出mvp的优点,呈详,目前在去哪儿网就职,同时也是csdn博客专家,他的blog地址:http://blog.csdn.net/p106786860.【阅读原文】,可看对应文章链接,话不多说,看下正文。
一、MVC
1.简介
MVC是目前大多数企业采用J2EE的结构设计,主要适用于交互式的Web应用。在Android中也有体现和使用,但是存在一定的弊端(下面将讲述),于是才有了Android官方推荐的MVP。
在Android的开发过程中,每个层对应如下:
Model层:对应Java Bean、Database、SharePreference和网络请求等;
View层:对应xml布局、自定义View或ViewGroup;
Controller层:对应Activity、Fragment;
2.实践
对于理论的理解 ,还是需要结合实际。下面我们将前面文章实现的https登录Demo,使用MVC的方式来进行重构:
项目结构:
View层:
activity_login.xml
LoginInputView.java
public class LoginInputView extends LinearLayout {
private TextView title;
private EditText content;
public LoginInputView(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layoutInflater.inflate(R.layout.inputview_login, this);
title = (TextView) findViewById(R.id.input_title);
content = (EditText) findViewById(R.id.input_content);
}
/**
* 设置输入项目的标题
* @param title 标题
*/
public void setTitle(String title) {
this.title.setText(title);
}
/**
* 获取用户输入的内容
* @return 用户输入的内容
*/
public String getContent() {
return content.getText().toString();
}
}
Model层:
LoginModel.java
public interface LoginModel {
LoginResult loginByUserNameAndPassword(Context context, LoginParam loginParam);
}
LoginModelImp.java
public interface LoginModel {
LoginResult loginByUserNameAndPassword(Context context, LoginParam loginParam);
}
Controller层:
LoginActivity.java
public class LoginActivity extends AppCompatActivity implements View.OnClickListener {
//View层渲染用户登录页面 组件
private LoginInputView userNameInput;
private LoginInputView passWordInput;
private Button loginButton;
private TextView responseTextView;
//Modle层提封装了登录请求数据和行为
private LoginModel loginModel;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
//Controller层获取Modle更新变化,选择到合适的视图更新显示
Bundle bundle = msg.getData();
LoginResult loginResult = (LoginResult) bundle.getSerializable("result");
responseTextView.setText(loginResult.getMessage());
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
userNameInput = (LoginInputView) findViewById(R.id.login_intput_username);
passWordInput = (LoginInputView) findViewById(R.id.login_intput_password);
loginButton = (Button) findViewById(R.id.login_login_button);
responseTextView = (TextView) findViewById(R.id.login_result_text);
loginButton.setOnClickListener(this);
userNameInput.setTitle("UserName:");
passWordInput.setTitle("PassWord:");
loginModel = new LoginModelImp();
}
@Override
public void onClick(View v) {
//接受从View层获取的用户点击,分发到Controller处理
responseTextView.setText("");
//Controller层从View层选择视图,获取用户输入
final String userName = userNameInput.getContent();
final String passWorld = passWordInput.getContent();
new Thread(new Runnable() {
@Override
public void run() {
//Controller层将用户输入登录信息,发送到Model层执行登录相关逻辑
LoginParam loginParam = new LoginParam(userName,passWorld);
LoginResult loginResult = loginModel.loginByUserNameAndPassword(LoginActivity.this,loginParam);
//Model层获取登录信息后,通知Controller层更新UI
Message message = handler.obtainMessage();
message.what = 1;
Bundle bundle = new Bundle();
bundle.putSerializable("result", loginResult);
message.setData(bundle);
handler.sendMessage(message);
}
}).start();
}
}
运行结果:
4.缺点
然而,在Android中由于View层的XML控制太弱,Controler层的Activity并没有和View层完全分离。当需要动态改变一个页面的显示(如背景、显示隐藏按钮等),都无法在xml中处理,只能在Activity中处理。造成了Activity即时Controller层又是View层,代码繁冗。二、MVP
1.简介
Model层:同MVC,负责处理数据加载或者存储,如从网络或者数据库获取数据等;
View层:处理数据展示,用户的交互。在MVP中Activity,Fragment属于该层;
Presenter层:是Model层和View层的桥梁,从Model层中获取数据,展示在View层;
2.实践项目结构:
Model层:同上mvc
View层:同上mvc,但activity在mvp中为view层,重构如下:
public class LoginActivity extends AppCompatActivity implements View.OnClickListener, LoginContract.View {
private LoginInputView userNameInput;
private LoginInputView passWordInput;
private Button loginButton;
private TextView responseTextView;
private Handler handler = new LoginHander();
private LoginContract.Presenter loginPesenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
userNameInput = (LoginInputView) findViewById(R.id.login_intput_username);
passWordInput = (LoginInputView) findViewById(R.id.login_intput_password);
loginButton = (Button) findViewById(R.id.login_login_button);
responseTextView = (TextView) findViewById(R.id.login_result_text);
loginPesenter = new LoginPresenter(new LoginModelImp(), this);
}
@Override
protected void onResume() {
super.onResume();
loginPesenter.start();
}
@Override
public void onClick(View v) {
loginPesenter.doLoginRequest(LoginActivity.this);
}
@Override
public void setPresenter(LoginContract.Presenter presenter) {
loginPesenter = presenter;
}
@Override
public void initLoginShow() {
userNameInput.setTitle("UserName:");
passWordInput.setTitle("PassWord:");
loginButton.setOnClickListener(this);
}
@Override
public LoginParam getInputLoginParam() {
final String userName = userNameInput.getContent();
final String passWorld = passWordInput.getContent();
LoginParam loginParam = new LoginParam(userName, passWorld);
return loginParam;
}
@Override
public void sendShowLoginMessage(LoginResult loginResult) {
Message message = handler.obtainMessage();
message.what = 1;
Bundle bundle = new Bundle();
bundle.putSerializable("result", loginResult);
message.setData(bundle);
handler.sendMessage(message);
}
@Override
public void updateLoginResultByMessage(Message message) {
Bundle bundle = message.getData();
LoginResult loginResult = (LoginResult) bundle.getSerializable("result");
updateLoginResultByString(loginResult.getMessage());
}
@Override
public void updateLoginResultByString(String result) {
responseTextView.setText(result);
}
/**
* 登录Handler,处理来自子线程更新登录页面的消息
*/
private class LoginHander extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
updateLoginResultByMessage(msg);
break;
}
}
}
}
Presenter层:
BasePresenter.java
public interface BasePresenter {
void start();
}
BaseView.java
public interface BaseView {
void setPresenter(T presenter);
}
LoginContract.java
public interface LoginContract {
interface View extends BaseView {
/**
* 初始化登录页面显示
*/
void initLoginShow();
/**
* 获取输入的登录参数
*/
LoginParam getInputLoginParam();
/**
* 发送显示登录结果消息
*/
void sendShowLoginMessage(LoginResult loginResult);
/**
* 通过消息更新登录结果
*/
void updateLoginResultByMessage(Message message);
/**
* 更新登录结果信息
*/
void updateLoginResultByString(String s);
}
interface Presenter extends BasePresenter {
/**
* 执行登录请求
*/
void doLoginRequest(Context context);
}
}
LoginPresenter.java
public class LoginPresenter implements LoginContract.Presenter {
private final LoginModel loginModel;
private final LoginContract.View loginView;
public LoginPresenter(LoginModel loginModel, LoginContract.View loginView) {
this.loginModel = loginModel;
this.loginView = loginView;
loginView.setPresenter(this);
}
@Override
public void start() {
loginView.initLoginShow();
}
@Override
public void doLoginRequest(final Context context) {
loginView.updateLoginResultByString("");
new Thread(new Runnable() {
@Override
public void run() {
LoginParam loginParam = loginView.getInputLoginParam();
LoginResult loginResult = loginModel.loginByUserNameAndPassword(context, loginParam);
loginView.sendShowLoginMessage(loginResult);
}
}).start();
}
}
3.优点
降低耦合度,实现了Model和View真正的完全分离,可以修改View而不影响Model;
Activity只处理生命周期的任务,代码变得简洁;
4.代码库
QProject:https://github.com/Pengchengxiang/QProject 分支:feature/mvc_mvp
第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。