浅谈安卓MVP模式

浅谈安卓MVP模式对于 MVP ModelViewPre 大多数人都能说出一二 MVC 的演化版本 让 Model 和 View 完全解耦 等等 本篇博文通过对 google 官方 demo https github com googlesample android architecture tree todo mvp 的理解 用自己的 demo 更好的讲解 mvp 的概念 帮助大家如何针对一个 Activity 页

本篇博文通过对google官方demo:https://github.com/googlesamples/android-architecture/tree/todo-mvp/的理解,用自己的demo更好的讲解mvp的概念,帮助大家如何针对一个Activity页面去编写针对MVP风格的代码。

一、MVP模式介绍

随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责。为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互,同时让Model只关系数据的处理,基于MVC概念的MVP(Model-View-Presenter)模式应运而生。

在MVP模式里通常包含4个要素:

(1) View :负责绘制UI元素、与用户进行交互(在Android中体现为Activity);

(2) View interface :需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试;

(3) Model :负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合);

(4) Presenter :作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。

这里写图片描述


二、为什么使用MVP模式

在Android开发中,Activity并不是一个标准的MVC模式中的Controller, 它的首要职责是加载应用的布局和初始化用户界面,并接受并处理来自用户的操作请求,进而作出响应。随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大臃肿。当我们将其中复杂的逻辑处理移至另外的一个类(Presenter)中时,Activity其实就是MVP模式中View,它负责UI元素的初始化,建立UI元素与Presenter的关联(Listener之类),同时自己也会处理一些简单的逻辑(复杂的逻辑交由Presenter处理).

另外,回想一下你在开发Android应用时是如何对代码逻辑进行单元测试的?是否每次都要将应用部署到Android模拟器或真机上,然后通过模拟用户操作进行测试?然而由于Android平台的特性,每次部署都耗费了大量的时间,这直接导致开发效率的降低。而在MVP模式中,处理复杂逻辑的Presenter是通过interface与View(Activity)进行交互的,这说明了什么?说明我们可以通过自定义类实现这个interface来模拟Activity的行为对Presenter进行单元测试,省去了大量的部署及测试的时间。


三、MVP与MVC的异同

MVC模式与MVP模式都作为用来分离UI层与业务层的一种开发模式被应用了很多年。在我们选择一种开发模式时,首先需要了解一下这种模式的利弊:

无论MVC或是MVP模式都不可避免地存在一个弊端:额外的代码复杂度及学习成本。

但比起他们的优点,这点弊端基本可以忽略了:

(1)降低耦合度 (2)模块职责划分明显 (3)利于测试驱动开发 (4)代码复用 (5)隐藏数据 (6)代码灵活性 

对于MVP与MVC这两种模式,它们之间也有很大的差异。有一些程序员选择不使用任何一种模式,有一部分原因也许就是不能区分这两种模式差异。以下是这两种模式之间最关键的差异:

MVP模式:

1.View不直接与Model交互 ,而是通过与Presenter交互来与Model间接交互 2.Presenter与View的交互是通过接口来进行的,更有利于添加单元测试 3.通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑 

MVC模式:

1.View可以与Model直接交互 2.Controller是基于行为的,并且可以被多个View共享 3.可以负责决定显示哪个View 

四、利用MVP进行Android开发的例子

用mvp模式实现:通过输入用户名跟密码,点击登录,登录成功后,清除输入信息,并提示成功。

  1. 运行效果先看下:
    这里写图片描述 这里写图片描述

/ * 
     <功能详细描述>
       * * @author caoyinfei * @version [版本号, 2016/5/4] * @see [相关类/方法] * @since [产品/模块版本] */ public class LoginResponse { private String resutlCode; private String resultInfo; public String getResutlCode() { return resutlCode; } public String getResultInfo() { return resultInfo; } public void setResutlCode(String resutlCode) { this.resutlCode = resutlCode; } public void setResultInfo(String resultInfo) { this.resultInfo = resultInfo; } } 
     
/ * @Title: * @Description: 模型抽象类 * @Author:cyf * @Since:2016/11/23 * @Version: */ public interface LoginTaskDataSource { / * 保存登录信息到本地 * @param loginResponse */ void saveLoginResponse(LoginResponse loginResponse); / * 登录请求 * @param loginReq * @param callback */ void login(LoginReq loginReq, NetTasksCallback callback); / * 网络请求回调 */ interface NetTasksCallback { void onSuccess(LoginResponse loginResponse); void onFailure(String errorMsg); } } 
/ * model层,处理数据(包括网络,数据库等等) * 主要就是把presenter层拆分,以后网络框架,数据库换框架,不用动业务代码,只需要改Repository层代码 */ public class LoginTasksRepository implements LoginTaskDataSource { @Override public void saveLoginResponse(LoginResponse loginResponse) { } @Override public void login(LoginReq loginReq, NetTasksCallback callback) { //开启网络请求 / * 网络请求,这边暂时省略 */ //成功后,回调到presenter层中 LoginResponse response = new LoginResponse(); response.setResultInfo(loginReq.getName() + "====" + loginReq.getPassword() + "====登录成功"); response.setResutlCode("0"); callback.onSuccess(response); } } 
/ * 
     <功能详细描述>
       * * @author caoyinfei * @version [版本号, 2016/5/4] * @see [相关类/方法] * @since [产品/模块版本] */ public interface IMvpView { void onError(String errorMsg, String code); void onSuccess(); void showLoading(); void hideLoading(); } 
     
public interface IUserLoginView extends IMvpView { void clearEditContent(); } 
/ * 
     <基础业务类>
       * * @author caoyinfei * @version [版本号, 2016/6/6] * @see [相关类/方法] * @since [V1] */ public interface Presenter 
      
        { void attachView(V view); void detachView(V view); String getName(); } 
       
     
/ * 
     <基础业务类>
       * * @author caoyinfei * @version [版本号, 2016/6/6] * @see [相关类/方法] * @since [V1] */ public abstract class BasePresenter 
      
        implements Presenter 
       
         { protected V mvpView; public void attachView(V view) { mvpView = view; } @Override public void detachView(V view) { mvpView = null; } @Override public String getName() { return mvpView.getClass().getSimpleName(); } } 
        
       
     
/ * 
     <绑定view层和model层>
       * * @author caoyinfei * @version [版本号, 2016/5/4] * @see [相关类/方法] * @since [产品/模块版本] */ public class LoginPresenter extends BasePresenter 
      
        { private LoginTasksRepository loginTaskDataSource; public LoginPresenter() { loginTaskDataSource = new LoginTasksRepository(); } public void login(LoginReq loginReq) { loginTaskDataSource.login(loginReq, new LoginTaskDataSource.NetTasksCallback() { @Override public void onSuccess(LoginResponse loginResponse) { mvpView.hideLoading(); mvpView.onSuccess(loginResponse); } @Override public void onFailure(String errorMsg) { } }); } } 
       
     

5)activity实现代码

/ * 
     <登录界面>
       * * @author caoyinfei * @version [版本号, 2016/5/4] * @see [相关类/方法] * @since [产品/模块版本] */ public class MainActivity extends Activity implements View.OnClickListener, IUserLoginView { / * 用户名 */ private EditText userName; / * 密码 */ private EditText passWord; / * 登录按钮 */ private Button button; private LoginPresenter mUserLoginPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); loadData(); } / * 初始化布局组件 */ private void initView() { userName = (EditText) findViewById(R.id.user_name); passWord = (EditText) findViewById(R.id.password); button = (Button) findViewById(R.id.login); / * 添加按钮点击事件 */ button.setOnClickListener(this); } / * 初始化数据 */ private void loadData() { mUserLoginPresenter = new LoginPresenter(); mUserLoginPresenter.attachView(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.login: LoginReq loginReq = new LoginReq(); loginReq.setName(userName.getText().toString()); loginReq.setPassword(passWord.getText().toString()); mUserLoginPresenter.login(loginReq); break; default: break; } } @Override public void onError(String errorMsg, String code) { } @Override public void onSuccess(Object response) { if (response instanceof LoginResponse) { LoginResponse loginResponse = (LoginResponse) response; Toast.makeText(this, loginResponse.getResultInfo(), 1).show(); } } @Override public void showLoading() { } @Override public void hideLoading() { } @Override public void clearEditContent() { userName.setText(""); passWord.setText(""); } @Override protected void onDestroy() { mUserLoginPresenter.detachView(this);//Presenter对象持有Activity对象,这条GC链不剪断,Activity就无法被完整回收,造成内存泄漏 super.onDestroy(); } } 
     


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/217695.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 上午8:58
下一篇 2026年3月18日 上午8:59


相关推荐

  • AI大模型实用(三)Java快速实现智能体整理(Springboot+LangChain4j)

    AI大模型实用(三)Java快速实现智能体整理(Springboot+LangChain4j)

    2026年3月15日
    2
  • mysql数据库引擎常用面试总结

    mysql数据库引擎常用面试总结一次面试被问到了 MySQL 的相关问题 一个是对 mysql 了解多少 引擎有什么 主要区别是什么 第二个被问的更多 给你一个百万级别的表怎么查询优化 今天整理了一些资料回答第一个问题 总的思想 MyIASM 引擎是为了查和增加 效率高 所有功能都围绕这这个 Innodb 引擎功能更强 事务等 效率低一些 MySQL 数据库引擎详解

    2026年3月17日
    2
  • RC微积分电路

    RC微积分电路br 9 nbsp RC 一阶电路 动态特性 nbsp nbsp 频率响应 br nbsp nbsp br nbsp nbsp nbsp 一个电阻和一个电容串联起来的 RC 电路看起来是很简单的电路 实际上其中的现象已经相当复杂 这些现象涉及到的概念和分析方法 是电子电路中随处要用到的 务必仔细领悟 br nbsp br 9 1 nbsp 零输入响应 br nbsp br 1 电容上电压的过渡过程 br nbsp br 先从数学上最简单的情形来看 RC 电路的特性 在图 9 1 nbsp 中 描述了问题的物理模型 假定 RC 电路接在一个电压值为 V 的直流电源上很长的时间了 电容上的电

    2026年3月18日
    2
  • Hunyuan-HY-MT1.8B部署教程:Windows环境适配方案

    Hunyuan-HY-MT1.8B部署教程:Windows环境适配方案

    2026年3月12日
    3
  • php 四舍五入到分,PHP四舍五入精确小数位及取整

    php 四舍五入到分,PHP四舍五入精确小数位及取整经常用到取整的函数 今天小小的总结一下 其实很简单 就是几个函数而已 主要是 ceil floor round intval 进一法取整 四舍五入取整 忽略小数等的取整数方法大全 PHP 取整数函数常用的四种方法 下面收集了四个函数 经常用到取整的函数 今天小小的总结一下 其实很简单 就是几个函数而已 主要是 ceil floor round intvalPHP 取整数函数常用的四种方法 下面收集了四

    2026年3月18日
    2
  • 基于fpga的spi通信设计_协议的概念

    基于fpga的spi通信设计_协议的概念一、SPI协议1、SPI协议概括SPI(SerialPeripheralInterface)——串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在EEPROM、FLASH、实时时钟,AD转换器以及数字信号处理器和数字信号解码器之间。SPI是一种高速,全双工,同步的通信总线,在芯片上只占用四根线(CS、MOSI、MISO、SCK),极大的…

    2022年10月15日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号