记一次重构:Android实践从MVC架构到MVP架构
一直以来,想分享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
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.qunar.hotel.controller.LoginActivity"> <!--登录输入用户名--> <com.qunar.hotel.view.LoginInputView android:id="@+id/login_intput_username" android:layout_width="match_parent" android:layout_height="wrap_content" /> <!--登录输入密码--> <com.qunar.hotel.view.LoginInputView android:id="@+id/login_intput_password" android:layout_width="match_parent" android:layout_height="wrap_content" /> <!--登录按钮--> <Button android:id="@+id/login_login_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Login" /> <!--登录结果文案--> <TextView android:id="@+id/login_result_text" android:layout_width="match_parent" android:layout_height="wrap_content" /></LinearLayout>
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(); }}
运行结果:
3.优点
Controller层起到桥梁作用,在View层和Model层之间通信,使得View层和Modle层分离解耦;
4.缺点
然而,在Android中由于View层的XML控制太弱,Controler层的Activity并没有和View层完全分离。当需要动态改变一个页面的显示(如背景、显示隐藏按钮等),都无法在xml中处理,只能在Activity中处理。造成了Activity即时Controller层又是View层,代码繁冗。
二、MVP
1.简介
MVP模式是MVC模式在Android上的一种变体。在MVC中Activity应该是属于Controller层,而实质上,它即承担了Contrller,也包含了许多View层的逻辑在里面。把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model类还是原来的Model,这就是MVP;
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<T> { void setPresenter(T presenter);}
LoginContract.java
public interface LoginContract { interface View extends BaseView<Presenter> { /** * 初始化登录页面显示 */ 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
发布评论
热门评论区: