前言

开始写这篇这前确实挺纠结的,纠结到底要不要写。一个是这种设计模式的内容网上都已经很多了,都是前辈写的,我写的可能也不如他们好。二是设计模式这种东西很大,高大上的原理就够你喝一壶的了,光一篇博客想要理解还是挺难的。纠结到最后还是决定写了,就当给自己这几天的学习做下笔记,也省的自己再去纠结了,行动就是最好的解决办法。

原理

MVP是有MVC演变而来的,因为在Android中用MVC来写的时候,Activity到底是View层还是Controller层真的是傻傻分不去清楚,所以基本它就把这两层的活都自己一个人干了,这样一来Activity上的代码量会非常大,Controller层和View层没有实现解耦分离开来,如果后面这两层代码其中一层出了问题你就可能两层都的修改,代码是没什么重用性可言的。而MVP的出现就是为了解决这些问题的,MVP模式将Controller的工作抽取出来交给Presenter层,Presenter层负责控制处理业务逻辑,作为中间层建立起Model层和View层的联系,从而实现三层交互。这么说确实挺难让人理解的,下面就通过一个例子,在例子中夹杂着解释让我们掌握MVP的用法吧。

MVP.png

这个图很简单,当 View 需要更新数据时,首先去找 Presenter,然后 Presenter 去找 Model 请求数据,Model 获取到数据之后通知 Presenter,Presenter 再通知 View 更新数据,这样 Model 和 View 就不会直接交互了,所有的交互都由 Presenter 进行,Presenter 充当了桥梁的角色。很显然,Presenter 必须同时持有 View 和 Model 的对象的引用,才能在它们之间进行通信。

例子

该例子就是一个模拟登陆的例子,一看效果图你就立马明白了,效果图如下:

mvp.gif

Model层

创建用于保存用户信息的实体类User

public class User {

private String username;

private String password;

public String getUsername() {

return username;

}

public void setUsername(String username) {

this.username = username;

}

public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

}

结果回调接口

public interface OnLoginListener {

//登录成功的回调

void loginSuccess(User user);

//登录失败的回调

void loginFailed();

}

定义业务接口,这里抽出一个接口的目的是为了降低一层耦合和便于复用。比如同一个网络请求业务你可以有OkHttp的实现,同时也可以有Retrofit的实现,这样一来就可以方便的做到网络框架的替换,当然你还可以有不同内容的实现。

public interface IUserBiz {

//登录方法

public void login(String username, String password, OnLoginListener loginListener);

}

具体Model的实现类

public class UserBiz implements IUserBiz {

@Override

public void login(final String username, final String password, final OnLoginListener loginListener) {

//模拟网络请求耗时操作

new Thread() {

@Override

public void run() {

//模拟了耗时

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//模拟登录成功

if ("luoqiang".equals(username) && "108".equals(password)) {

User user = new User();

user.setUsername(username);

user.setPassword(password);

loginListener.loginSuccess(user);

} else {

loginListener.loginFailed();

}

}

}.start();

}

}

View层

定义View层接口,View层不关心数据,不关心逻辑处理!只关心和用户的交互,那么这个登录界面应该有的操作就是:从输入框获取用户名,获取密码,清除用户名,清除密码,登录网络请求时显示进度条,隐藏进度条,登录成功跳转到对应界面,登录失败提示。接下来定义接口如下:

public interface IUserLoginView {

String getUserName();

String getPassword();

void clearUserName();

void clearPassword();

void showLoading();

void hideLoading();

void toMainActivity(User user);

void showFaileTips();

}

为什么要写一个 View 的接口,直接把 Activity 本身传入到 Presenter 层不行吗?这当然是可行的,这里使用接口的目的和Model层使用接口的目的是一样的,主要是为了代码的复用,试想一下,如果直接传入 Activity,那么这个 Presenter 就只能为这一个 Activity 服务。举个例子,假设有个 App 已经开发完成了,可以在手机上正常使用,现在要求做平板上的适配,在平板上的界面显示效果有所变化,TextView 并不是直接在 Activity 中的,而是在 Fragment 里面,如果没有使用 View 的接口的话,那就需要再写一个针对 Fragment 的 Presenter,然后把整个过程再来一遍。但是使用 View 的接口就很简单了,直接让 Fragment 实现这个接口,然后复写接口里面的方法,Presenter 和 Model 层都不需要做任何改动。

View的具体实现类,其实就是Activity。这里需要注意内存泄漏的风险。试想一下,如果在点击登录按钮之后,Model 获取到数据之前,退出了 Activity,此时由于 Activity 被 Presenter 引用,而 Presenter 正在进行耗时操作,会导致 Activity 的对象无法被回收,造成了内存泄漏,解决的方式很简单,在 Activity 退出的时候,把 Presenter 中 View 的引用置为空即可。

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.ProgressBar;

import android.widget.Toast;

public class UserLoginActivity extends AppCompatActivity implements IUserLoginView {

private EditText mEtUsername, mEtPassword;

private Button mBtnLogin, mBtnClear;

private ProgressBar mPbLoading;

private UserLoginPresenter mUserLoginPresenter;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mUserLoginPresenter = new UserLoginPresenter(this);

initViews();

}

private void initViews() {

mEtUsername = (EditText) findViewById(R.id.et_username);

mEtPassword = (EditText) findViewById(R.id.et_password);

mBtnClear = (Button) findViewById(R.id.btn_clear);

mBtnLogin = (Button) findViewById(R.id.btn_login);

mPbLoading = (ProgressBar) findViewById(R.id.pb_loading);

mBtnLogin.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

mUserLoginPresenter.login();

}

});

mBtnClear.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

mUserLoginPresenter.clear();

}

});

}

@Override

public String getUserName() {

return mEtUsername.getText().toString();

}

@Override

public String getPassword() {

return mEtPassword.getText().toString();

}

@Override

public void clearUserName() {

mEtUsername.setText("");

}

@Override

public void clearPassword() {

mEtPassword.setText("");

}

@Override

public void showLoading() {

mPbLoading.setVisibility(View.VISIBLE);

}

@Override

public void hideLoading() {

mPbLoading.setVisibility(View.GONE);

}

@Override

public void toMainActivity(User user) {

Toast.makeText(this, "跳转到登录成功页面", Toast.LENGTH_SHORT).show();

}

@Override

public void showFaileTips() {

Toast.makeText(this, "登录失败", Toast.LENGTH_SHORT).show();

}

@Override

protected void onDestroy() {

super.onDestroy();

//为了防止内存泄漏,解绑Presenter层对View层的引用

mUserLoginPresenter.detachView();

}

}

对于在Activity中实现我们上述定义的接口,是一件很容易的事,毕竟接口引导我们去完成。

Presenter层

Presenter的作用就是从View层获取用户的输入,传递到Model层进行处理,然后回调给View层,输出给用户!

import android.os.Handler;

public class UserLoginPresenter {

private IUserBiz userBiz;

private IUserLoginView userLoginView;

private Handler mHandler = new Handler();

//对应视图页面销毁的标志位,当视图销毁后回调就不需要处理了

private boolean destroyFlag;

//Presenter必须要能拿到View和Model的实现类

public UserLoginPresenter(IUserLoginView userLoginView) {

this.userLoginView = userLoginView;

this.userBiz = new UserBiz();

}

public void login() {

userLoginView.showLoading();

userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() {

@Override

public void loginSuccess(final User user) {

if (!destroyFlag) { //View层销毁后不需要处理的判断

//需要在UI线程执行

mHandler.post(new Runnable() {

@Override

public void run() {

userLoginView.toMainActivity(user);

userLoginView.hideLoading();

}

});

}

}

@Override

public void loginFailed() {

if (!destroyFlag) { //View层销毁后不需要处理的判断

//需要在UI线程执行

mHandler.post(new Runnable() {

@Override

public void run() {

userLoginView.showFaileTips();

userLoginView.hideLoading();

}

});

}

}

});

}

public void clear() {

userLoginView.clearUserName();

userLoginView.clearPassword();

}

//解绑视图

public void detachView() {

destroyFlag = true;

this.userLoginView = null;

}

}

presenter完成二者的交互,那么肯定需要二者的实现类。大致就是从View中获取需要的参数,交给Model去执行业务方法,执行的过程中需要的反馈,以及结果,再让View进行做对应的显示。整个过程中,View 和 Model 并没有直接交互,所有的交互都是在 Presenter 中进行的。

感谢

android mvp模式到底好,Android中MVP设计模式相关推荐

  1. android mvp模式鸿洋,Android上的MVP模式

    什么是MVP? MVP模式可以分离显示层和逻辑层,所以功能接口如何工作与功能的展示可以实现分离,MVP模式理想化地可以实现同一份逻辑代码搭配不同的显示界面.首先要澄清就是MVP不是一个结构化的模式,它 ...

  2. android中的mvp模式怎么定义,详解MVP模式在Android开发中的应用

    一.MVP介绍 随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责.为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互,同时让Mode ...

  3. Android开发模式万佛朝中MVX(MVC、MVP、MVVM)

    今天公司的测试服务器开小差了,后台被吐槽的体无完肤,虽然我们都知道跟他没有什么关系,但是生活需要乐趣,总要有人背锅,哈哈!~~~暂时没有环境开发了,那就分享一下之前做的一篇关于Android开发模式的 ...

  4. 浅谈Android中MVP模式用于实际项目中的问题与优化

    学习MVP不算久,前段时间才把公司的两个项目完全转换为MVP模式,改了下来,略有心得,给大家分享一下. 才开始学习使用MVP时,看到大家说了很多MVP的优点,代码复用,条理清晰等等.不过我改下来发现, ...

  5. android mvp模式例子_Android开发中的MVP模式概念以及网络请求实现顺序

    MVP模式 三层 1.view 2.presenter 3.model View 1.view职责,继承view接口,实现方法,持有presenter,this传入presenter Presente ...

  6. android 夜间模式源码,Android Support Library 之 夜间模式

    前言 夜间模式实现方式:1.通过切换theme来实现夜间模式.优点:可以匹配多套主题,并不局限于黑白模式缺点:需要大量定义主题详见博客:http://wuxiaolong.me/2015/08/19/ ...

  7. android地图模式,百度地图Android V2.0新增卫星图及3D模式功能

    近日,百度地图Android版V2.0正式对外发布.本次升级引入了全新引擎.全新数据并采用全新界面,因此这个版本被称为全新跨代版.它不仅为用户打造更加优质精准的地图检索及浏览感受,同时还有效节省使用地 ...

  8. Flutter Candies 一桶天下,一个小例子彻底搞懂Android的MVP模式到底是什么

    | | | | | ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2lt ...

  9. android mvp模式例子_[Android] MVP设计模式及实例

    MVP简介 MVP 所对应的意义:M-Model-模型.V-View-视图.P-Presenter-主持人. MVP 的结构图如下所示,对于这个图理解即可而不必局限其中的条条框框,毕竟在不同的场景下多 ...

  10. android+飞行模式+收音机,Jelly Bean中的飞行模式

    我正尝试在Android 4.2.2中设置Nexus 4中的飞行模式. 我知道这是不可能的,因为AIRPLANE_MODE_ON被移动到Global system settings,它只是一个读取选项 ...

最新文章

  1. 双十一,程序员前女友发来消息。。。
  2. 电话光端机安装步骤详解
  3. Linux 用C/C++创建新文件并写入内容
  4. 如何实现消息功能_如何实现微信小程序的轮盘抽奖功能
  5. 2017前端技术大盘点 1
  6. 高质量外链该怎样做?
  7. oozie控制台命令
  8. 平面设计ps/ai/cdr
  9. 计算机主机显卡在哪里,台式电脑显卡在哪个位置 台式机显卡拆卸方法
  10. R 回归 虚拟变量na_【R语言进阶】Logistic回归及哑变量设置
  11. Linux SPI设备驱动
  12. 计算机增加一个硬盘怎么设置方法,电脑加硬盘【操作教程】
  13. 《强化学习与最优控制》学习笔记(一):确定性动态规划和随机性动态规划
  14. java ee学生管理系统_javaEE 学生基本信息管理系统
  15. 名医高效良方(三叉神经痛)
  16. 原来win10有免费的文字语音朗读功能,配音再不愁了
  17. 使用python爬取天气信息(包括历史天气数据)
  18. 大数据告诉你梅西如何制霸足坛!
  19. 这篇文章说到了程序化交易的本质!
  20. 【接口协议】FPGA 驱动 VGA 显示实验(二)实验设计部分

热门文章

  1. php 图像居中裁剪函数,php中自定义图像居中裁剪函数实现的代码案例
  2. spark java jar 依赖_spark提交依赖jar包的解决方法
  3. python中的fun_Python fun中*args,**kwargs参数的含义和用法(*args,**kwargs),Pythonfunargskwargs,及...
  4. nyoj 1172 unlucky number
  5. WampServer的下载方法-解决报错the installation folder chosen is not that wampserver
  6. 极客大学架构师训练营 JVM虚拟机原理 JVM垃圾回收原理 Java编程优化 第17课 听课总结
  7. Swift 5 Dictionary用法大全
  8. 图论基础(Tarjan与拓扑排序)
  9. java生成gif_Java生成动态GIF图片
  10. 使用一重循环打印乘法口诀