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来构建. 这篇文章是我通过研究和 ...
随机推荐
- sql 解释顺序
from:全量数据, where:数据过滤,生成新的虚表.个人主观上理解,where中的条件,如果涉及到join中的表,则会移动到相应的on条件中,减少后续生成的虚表大小. join:根据on中的条件 ...
- 微信小程序随笔。笔记本忘家里了
所有ui组件都有的共有属性id.class.style.hidden.data-*.bind* / catch*.
- android inflate压力泵,将视图发生整合的过程
转自:https://blog.csdn.net/u012702547/article/details/52628453?utm_source=copy inflate方法从大范围来看,分两种,三个参 ...
- 三分钟理解Java中字符串(String)的存储和赋值原理
可能很多Java的初学者对String的存储和赋值有迷惑,以下是一个很简单的测试用例,你只需要花几分钟时间便可理解. 1.在看例子之前,确保你理解以下几个术语: 栈:由JVM分配区域,用于保存线程执行 ...
- POJ 1258(最小生成树+知识)
用kruskal算法,利用w[i]给r[i]间接排序,从而r[i]可以按照边大小保存序号,同时要判断是否在一个集合里面 #include <cstdio> #include <ios ...
- spss C# 二次开发 学习笔记(三)——Spss .Net 开发
Spss .Net 二次开发的学习过程暂停了一段时间,今天开始重启. 之前脑残的不得了,本想从网上下载一个Spss的安装包,然后安装学习.于是百度搜索Spss,在百度搜索框的列表中看到Spss17.S ...
- Spring学习(一)---依赖注入和控制反转
Spring Spring是一个从实际开发中抽出来的框架,因此它完成了大量开发中的通用步骤,留给开发者的仅仅是与特定应用相关的部分,从而大大提高了企业应用的开发效率. Spring为企业应用的开发提供 ...
- jquery怎么取得有好几个并且name是相同的值
jQuery("input[name='number']").each(function(){ alert(jQuery(this).val()); });
- 第三天-基本数据类型 int bool str
# python基础数据类型 # 1. int 整数 # 2.str 字符串.不会用字符串保存大量的数据 # 3.bool 布尔值. True, False # 4.list 列表(重点) 存放大量的 ...
- ajax异步上传图片三种方案
转自:http://www.jb51.net/article/51180.htm 注:自己尝试了前两种,都可用: 目前常用的异步文件上传功能有几种,比较多见的如使用iframe框架形式,ajax功能效 ...