一直以来,想分享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.LoginInputViewandroid:id="@+id/login_intput_username"android:layout_width="match_parent"android:layout_height="wrap_content" /><!--登录输入密码--><com.qunar.hotel.view.LoginInputViewandroid:id="@+id/login_intput_password"android:layout_width="match_parent"android:layout_height="wrap_content" /><!--登录按钮--><Buttonandroid:id="@+id/login_login_button"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Login" /><!--登录结果文案--><TextViewandroid: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() {@Overridepublic 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;}}};@Overrideprotected 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();}@Overridepublic void onClick(View v) {//接受从View层获取的用户点击,分发到Controller处理responseTextView.setText("");//Controller层从View层选择视图,获取用户输入final String userName = userNameInput.getContent();final String passWorld = passWordInput.getContent();new Thread(new Runnable() {@Overridepublic void run() {//Controller层将用户输入登录信息,发送到Model层执行登录相关逻辑LoginParam loginParam = new LoginParam(userName,passWorld);LoginResult loginResult = loginModel.loginByUserNameAndPassword(LoginActivity.this,loginParam);//Model层获取登录信息后,通知Controller层更新UIMessage 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;@Overrideprotected 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);}@Overrideprotected void onResume() {super.onResume();loginPesenter.start();}@Overridepublic void onClick(View v) {loginPesenter.doLoginRequest(LoginActivity.this);}@Overridepublic void setPresenter(LoginContract.Presenter presenter) {loginPesenter = presenter;}@Overridepublic void initLoginShow() {userNameInput.setTitle("UserName:");passWordInput.setTitle("PassWord:");loginButton.setOnClickListener(this);}@Overridepublic LoginParam getInputLoginParam() {final String userName = userNameInput.getContent();final String passWorld = passWordInput.getContent();LoginParam loginParam = new LoginParam(userName, passWorld);return loginParam;}@Overridepublic 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);}@Overridepublic void updateLoginResultByMessage(Message message) {Bundle bundle = message.getData();LoginResult loginResult = (LoginResult) bundle.getSerializable("result");updateLoginResultByString(loginResult.getMessage());}@Overridepublic void updateLoginResultByString(String result) {responseTextView.setText(result);}/*** 登录Handler,处理来自子线程更新登录页面的消息*/private class LoginHander extends Handler {@Overridepublic 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);}@Overridepublic void start() {loginView.initLoginShow();}@Overridepublic void doLoginRequest(final Context context) {loginView.updateLoginResultByString("");new Thread(new Runnable() {@Overridepublic 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干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。

记一次重构:Android实践从MVC架构到MVP架构相关推荐

  1. Android 架构优化~MVP 架构改造

    以前我写过一篇关于 MVP 架构的文章<Android架构-MVP架构在Android中的实践>. 随着业务的复杂化,我们会发现传统的 MVP 架构依然会有很多问题. 下面我将和大家一起探 ...

  2. Android官方MVP架构详解

    综述 对于MVP (Model View Presenter)架构是从著名的MVC(Model View Controller)架构演变而来的.而对于Android应用的开发中本身可视为一种MVC架构 ...

  3. Android MVP架构从入门到精通-真枪实弹

    Android MVP架构从入门到精通-真枪实弹 一. 前言 二. MVC架构 1. MVC架构优缺点 A. 缺点 B. 优点 三. MVP架构 1. MVP架构优缺点 A. 缺点 B. 优点 四. ...

  4. android静默卸载,Android实践 -- Android静默安装和卸载

    App的静默安装和卸载 Android系统本身提供了安装卸载功能,但是api接口是@hide的,不是公开的接口,所以在应用级别 是无法实现静默安装和卸载的,要实现静默安装和卸载需要是系统应用,要有系统 ...

  5. Android 系统(194)---Android实践 -- 设置系统日期时间和时区

    Android实践 -- 设置系统日期时间和时区 转自:https://www.cnblogs.com/Free-Thinker/p/6627813.html 设置系统日期时间和时区 设置系统的日期时 ...

  6. Android 设计模式之MVC,从一个实例中来理解MVC

    前言 已经有几天没有写过帖子了,主要前一段时间在忙公司的项目,现在闲下来想看一下其他的东西,然后从公司的iOS大神那里了解到了苹果的设计模式是MVC,于是想着自己也来写一下Android这边的MVC. ...

  7. Android中的MVC详解

    MVC无人不知,可很多程序员对MVC的概念的理解似乎有误,换言之他们一直在错用MVC,尽管即使如此软件也能被写出来,然而软件内部代码的组织方式却是不科学的,这会影响到软件的可维护性.可移植性,代码的可 ...

  8. android MVC,MVP架构

    前言 为什么要使用架构模式,有什么作用? 使用架构的目的是使程序模块化,做到模块内部的高聚合和模块之间的低耦合,降低各层之间的耦合性,达到解耦的目的,减少模块代码之间的相互影响,模块区域的分明,也更加 ...

  9. Android组件化实践2——经典组件化项目架构

    Android组件化实践2--经典组件化项目架构 京东的采用aab( Android App bundles)之后的架构图 安居客项目架构演化 参考:https://zhuanlan.zhihu.co ...

  10. 如何使用MVP架构Android应用项目

    目录 MVP简介 MVP结构 MVP与MVC区别 实战演习 正文 1.MVP简介 相信大家对MVC都是比较熟悉了:M-Model-模型.V-View-视图.C-Controller-控制器,MVP作为 ...

最新文章

  1. WebLogic自带的Web Service测试工具
  2. 面试问到 Redis 事务,我脸都绿了。。
  3. javascript 温故而知新 getBoundingClientRect
  4. Chrome Console Cookie 控制台操作命令
  5. iostat命令(转)
  6. 中国第一软件开发_我第一次开发企业软件中学到的知识
  7. java.lang.OutOfMemoryError: Java heap space异常
  8. Shell脚本 – 查看网络接口信息
  9. c 函数 字符串 find_如何在C ++中使用字符串find()
  10. Windows核心编程_将窗口嵌入到桌面图标下面不被遮挡 spy 分析过程
  11. 使用kubernetes 官网工具kubeadm部署kubernetes(使用阿里云镜像)
  12. css样式的基本定义
  13. win10 UWP RSS阅读器
  14. 网络疯传华为面试题:800公斤牛,过承重700公斤的桥,有答案了?
  15. 网站访问数据统计工具
  16. windows 无法格式化SD卡
  17. Python版实现12306抢票功能,真的能帮你抢到春运回家的票吗?
  18. k8s访问外部mysql_Kubernetes连接外部数据源
  19. Windows 11 系统下载,正式版尚未发布
  20. 火山安卓开发支付宝自动转账功能

热门文章

  1. 论文笔记_S2D.46_2013-3DV_基于点融合的动态场景实时三维重建
  2. 交叉熵损失的来源、说明、求导与pytorch实现
  3. AGV机器人(1)基于视觉避障的理论基础
  4. spring实战第五版总结
  5. Python基础 ( 六 ) —— 迭代器和生成器
  6. 软件测试:等价类划分-----EditBox问题增加文本框
  7. LINUX内核-等待队列
  8. 在VS2010中使用Git管理源代码
  9. 【OpenCV学习笔记】【教程翻译】一(基于SVM和神经网络的车牌识别概述)
  10. 【LeetCode】【字符串】题号:*344. 反转字符串