Java(Android)编程思想笔记03:在Android开发中使用MVP模式
1. MVP模式简介:
MVC模式相信大家肯定是比较熟悉的:M-Model-模型、V-View-视图、C-Controller-控制器。
MVP作为MVC的演化版本,那么类似的MVP所对应的意义:M-Model-模型、V-View-视图、P-Presenter-表示器。
从MVC和MVP两者结合来看:
Controlller/Presenter在MVC/MVP中都起着逻辑控制处理的角色,起着控制各业务流程的作用。
而 MVP与MVC最不同的一点是:Model与View是不直接关联的也是就Model与View不存在直接关系,这两者之间间隔着的是Presenter层,其负责调控 View与Model之间的间接交互。
在 Android中很重要的一点就是对UI的操作基本上需要异步进行也就是在MainThread中才能操作UI,所以对View与Model的切断分离是合理的。此外Presenter与View、Model的交互使用接口定义交互操作可以进一步达到松耦合也可以通过接口更加方便地进行单元测试。
所以也就有了这张图片(MVP和MVC的对比):
其实最明显的区别就是,MVC中是允许Model和View进行交互的,而MVP中很明显,Model与View之间的交互由Presenter完成。还有一点就是Presenter与View之间的交互是通过接口的(代码中会体现)。
因此,MVP具有以下优势:
(1)Model与View完全分离,我们可以修改View而不影响Model
(2)可以更高效地使用Model,因为所有的交互都发生在一个地方:Presenter内部
(3)我们可以将一个Presenter用于多个View,而不需要改变Presenter的逻辑。这个特性非常的有用,因为View的变化总是比Model的变化频繁。
(4)如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)。
MVP的缺点:
(1)造成类数量爆炸,代码复杂度和学习成本高,在某些场景下presenter的复用会产生接口冗余。
2. MVP模式应用代码示例:
工程结构如下:

(1)Model层:
提供我们想要展示在view层的数据和具体登陆业务逻辑处理的实现:
package com.himi.mvpdemo.model; import com.himi.mvpdemo.OnLoginFinishedListener; /**
* Created by hebao on 2016/07/24.
* Class Note:模拟登陆的操作的接口,实现类为LoginModelImpl.相当于MVP模式中的Model层
*/
public interface LoginModel {
void login(String username, String password, OnLoginFinishedListener listener);
}
package com.himi.mvpdemo.model; import com.himi.mvpdemo.OnLoginFinishedListener; import android.os.Handler;
import android.text.TextUtils;
/**
* Created by hebao on 2016/7/24.
* Class Note:延时模拟登陆(2s),如果名字或者密码为空则登陆失败,否则登陆成功
*/
public class LoginModelImpl implements LoginModel { @Override
public void login(final String username, final String password, final OnLoginFinishedListener listener) { new Handler().postDelayed(new Runnable() {
@Override public void run() {
boolean error = false;
if (TextUtils.isEmpty(username)){
listener.onUsernameError();//model层里面回调listener
error = true;
}
if (TextUtils.isEmpty(password)){
listener.onPasswordError();
error = true;
}
if (!error){
listener.onSuccess();
}
}
}, 2000);
}
}
(2)View层:
package com.himi.mvpdemo.view; /**
* Created by hebao on 2016/7/24.
* Class Note:登陆View的接口,实现类也就是登陆的activity
*/
public interface LoginView {
void showProgress(); void hideProgress(); void setUsernameError(); void setPasswordError(); void navigateToHome();
}
package com.himi.mvpdemo.view; import com.himi.mvpdemo.presenter.LoginPresenter;
import com.himi.mvpdemo.presenter.LoginPresenterImpl; import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast; public class LoginActivity extends Activity implements LoginView, View.OnClickListener { private ProgressBar progressBar;
private EditText username;
private EditText password;
private LoginPresenter presenter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login); progressBar = (ProgressBar) findViewById(R.id.progress);
username = (EditText) findViewById(R.id.username);
password = (EditText) findViewById(R.id.password);
findViewById(R.id.button).setOnClickListener(this); presenter = new LoginPresenterImpl(this);
} @Override
protected void onDestroy() {
presenter.onDestroy();
super.onDestroy();
} @Override
public void showProgress() {
progressBar.setVisibility(View.VISIBLE);
} @Override
public void hideProgress() {
progressBar.setVisibility(View.GONE);
} @Override
public void setUsernameError() {
username.setError(getString(R.string.username_error));
} @Override
public void setPasswordError() {
password.setError(getString(R.string.password_error));
} @Override
public void navigateToHome() {
// TODO startActivity(new Intent(this, MainActivity.class));
Toast.makeText(this,"login success",Toast.LENGTH_SHORT).show();
// finish();
} @Override
public void onClick(View v) {
presenter.validateCredentials(username.getText().toString(), password.getText().toString());
}
}
(3)Presenter层:
Presenter扮演着view和model的中间层的角色。获取model层的数据之后构建view层;也可以收到view层UI上的反馈命令后分发处理逻辑,交给model层做业务操作。它也可以决定View层的各种操作。
package com.himi.mvpdemo.presenter; /**
* Created by hebao on 2016/7/24. Class Note:登陆的Presenter
* 的接口,实现类为LoginPresenterImpl,完成登陆的验证,以及销毁当前view
*/
public interface LoginPresenter {
void validateCredentials(String username, String password); void onDestroy();
}
package com.himi.mvpdemo.presenter; import com.himi.mvpdemo.OnLoginFinishedListener;
import com.himi.mvpdemo.model.LoginModel;
import com.himi.mvpdemo.model.LoginModelImpl;
import com.himi.mvpdemo.view.LoginView; public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener {
private LoginView loginView;
private LoginModel loginModel; public LoginPresenterImpl(LoginView loginView) {
this.loginView = loginView;
this.loginModel = new LoginModelImpl();
} @Override
public void validateCredentials(String username, String password) {
if (loginView != null) {
loginView.showProgress();
} loginModel.login(username, password, this);
} @Override
public void onDestroy() {
loginView = null;
} @Override
public void onUsernameError() {
if (loginView != null) {
loginView.setUsernameError();
loginView.hideProgress();
}
} @Override
public void onPasswordError() {
if (loginView != null) {
loginView.setPasswordError();
loginView.hideProgress();
}
} @Override
public void onSuccess() {
if (loginView != null) {
loginView.navigateToHome();
}
}
}
(4)登录的回调接口:
package com.himi.mvpdemo; /**
* Created by hebao on 2016/7/24.
* Class Note:登陆事件监听
*/
public interface OnLoginFinishedListener { void onUsernameError(); void onPasswordError(); void onSuccess();
}
(5)部署程序到手机上,如下:

3. 示例代码流程(参考下面的图):
(1)Activity做了一些UI初始化的东西并需要实例化对应LoginPresenter的引用和实现 LoginView的接口,监听界面动作。
(2 )登陆按钮按下后即接收到登陆的事件,在onClick里接收到即通过LoginPresenter的引用把它交给LoginPresenter处理。LoginPresenter接收到了登陆的逻辑就知道要登陆了。
(3)然后LoginPresenter显示进度条并且把逻辑交给我们的Model去处理,也就是这里面的LoginModel,(LoginModel的实现类LoginModelImpl),同时会把OnLoginFinishedListener也就是LoginPresenter自身传递给我们的Model(LoginModel)。
(4)LoginModel处理完逻辑之后,结果通过OnLoginFinishedListener回调通知LoginPresenter。
(5)LoginPresenter再把结果返回给view层的Activity,最后activity显示结果。
请参考这张类图:
4. 注意事项:
(1)presenter里面还有个OnLoginFinishedListener,其在Presenter层实现,给Model层回调,更改View层的状态,确保 Model层不直接操作View层。
(2) 在一个好的架构中,model层可能只是一个领域层和业务逻辑层的入口,如果我们参考网上比较火的Uncle Bob clean architecture model层可能是一个实现业务用例的交互者,在后续的文章中应该会涉及到这方面的问题,目前能力有限。暂时讲解到这里.
5. MVP经典参考资料
请直接参考文章,这里面有很多的mvp模式的学习资料:
- android架构合集(请关注github,后续会不断更新)
- android mvp github地址(本篇博客正是参考这个项目进行讲解的。这个项目也很简单,分为login和main两个模块,总共十个类,思路非常清晰。学习的朋友可以直接clone查看源码。)

- androidmvp 的src代码分为login和main两个模块

本项目Github地址:https://github.com/PocketBoy/hebao/tree/master/MVPDEMO
Java(Android)编程思想笔记03:在Android开发中使用MVP模式的更多相关文章
- java编程思想笔记(1)
java编程思想笔记(1) 一,对象的创建和生命周期 对象的数据位于何处?怎样控制对象的生命周期? 在堆(heap)的内存池中动态地创建对象. java完全采用了动态内存分配方式. 二,垃圾回收器 自 ...
- Java编程思想 笔记
date: 2019-09-06 15:10:00 updated: 2019-09-24 08:30:00 Java编程思想 笔记 1. 四类访问权限修饰词 \ 类内部 本包 子类 其他包 publ ...
- Android:日常学习笔记(8)———探究UI开发(3)
Android:日常学习笔记(8)———探究UI开发(3) 详解四种基本布局 前言 布局定义用户界面的视觉结构,如Activity或应用小部件的 UI.您可以通过两种方式声明布局: 在 XML 中声明 ...
- JAVA GUI编程学习笔记目录
2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...
- Android:日常学习笔记(8)———探究UI开发(5)
Android:日常学习笔记(8)———探究UI开发(5) ListView控件的使用 ListView概述 A view that shows items in a vertically scrol ...
- Android:日常学习笔记(7)———探究UI开发(4)
Android:日常学习笔记(7)———探究UI开发(4) UI概述 View 和 ViewGrou Android 应用中的所有用户界面元素都是使用 View 和 ViewGroup 对象构建而成 ...
- Android:日常学习笔记(8)———探究UI开发(2)
Android:日常学习笔记(8)———探究UI开发(2) 对话框 说明: 对话框是提示用户作出决定或输入额外信息的小窗口. 对话框不会填充屏幕,通常用于需要用户采取行动才能继续执行的模式事件. 提示 ...
- Android:日常学习笔记(7)———探究UI开发(1)
Android:日常学习笔记(7)———探究UI开发(1) 常用控件的使用方法 TextView 说明:TextView是安卓中最为简单的一个控件,常用来在界面上显示一段文本信息. 代码: <T ...
- 转:Android开发中的MVP架构(最后链接资源不错)
Android开发中的MVP架构 最近越来越多的人开始谈论架构.我周围的同事和工程师也是如此.尽管我还不是特别深入理解MVP和DDD,但是我们的新项目还是决定通过MVP来构建. 这篇文章是我通过研究和 ...
随机推荐
- [转]asp.net权限认证:HTTP基本认证(http basic)
本文转自:http://www.cnblogs.com/lanxiaoke/p/6353955.html HTTP基本认证示意图 HTTP基本认证,即http basic认证. 客户端向服务端发送一个 ...
- 文件流FileStram类
本节课主要学习三个内容: 创建FileStram流 读取流 写入流 文件流FileStram类,是用来实现对文件的读取和写入.FileStram是操作字节的字节数组,当提供向文件读取和写入字节的方法时 ...
- C#RabbitMQ基础学习笔记
RabbitMQ基础学习笔记(C#代码示例) 一.定义: MQ是MessageQueue,消息队列的简称(是流行的开源消息队列系统,利用erlang语言开发).MQ是一种应用程序对应用程序的通信方法. ...
- Linux 中文乱码
开发中不免会接触到linux,Linux系统中文语言乱码也是我们常碰到的一个问题之一. 在网上查找了不少资料,参考了https://www.linuxidc.com/Linux/2017-07/145 ...
- zoj 3747 (DP)(连续至多,连续至少)
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5170 参考: http://blog.csdn.net/cc_again/ar ...
- jvm 内存机制
jvm 的内存包括stack ,Heap,NonHeap,在此重点说明Heap,NonHeap. Heap叫堆内存,它用于存放类实例和数组信息.NonHeap叫非堆内存,用于存放类,方法等可反射的对象 ...
- asp.net WebService的一个简单示例
不同的系统之间经常会需要数据的交换对接,而Web Service技术, 能使得运行在不同机器上的不同应用无须借助附加的.专门的第三方软件或硬件, 就可相互交换数据或集成.依据Web Service规范 ...
- react context toggleButton demo
//toggleButton demo: //code: //1.Appb.js: import React from 'react'; import {ThemeContext, themes} f ...
- js点击事件在苹果端失效的问题
在安卓机上,我们随意定义点击事件也能够在找到点击的元素,但是在苹果端上就是不行,怎么点击都没有效果.这是因为在苹果机上window禁止了手指误点功能,必须解除这一功能,或者给点击事件指引某个元素上绑定 ...
- 简析 Tomcat 、Nginx 与 Apache 的区别
简析 Tomcat .Nginx 与 Apache 的区别 本文讲的是简析 Tomcat .Nginx 与Apache的区别, 经常在用 apache 和 tomcat 等这些服务器,可是总感觉还是不 ...