摘要: 在MVVM成熟之前MVP模式在Android上有被神化的趋势,笔者曾经在商业项目中从零开始大规模采用过MVP模式对项目进行开发。在使用MVP模式进行开发的时候发现项目的结构模式对开发是有一定的影响的,在这里笔者会对这一问题进行探讨。希望通过这篇blog能让读者了解如何使用MVP模式搭建一个功能完善的MVP模式开发框架,避免一些笔者认为比较严重的问题。

为什么要使用MVP模式

在传统的Android开发中,我们一般是使用MVC模式进行开发的。
传统MVC模式介绍:

  1. View: 视图层,对应xml文件
  2. Controller: 控制层,对应Activity和Fragment层,进行数据处理
  3. Model:实体层,负责获取实体数据

在Android开发中采用MVC模式的一个最大的弊端就是xml作为View层视图能力实在太弱,所以一般情况下我们都是通过Controller层来辅助处理一些视图的。这样的结果就导致Controller既作为控制层的同时又承担了View层的大部分功能,采用MVC模式往往会导致Activity和Fragment中的代码非常复杂。我们将Android中采用的MVC模式称为MV模式更加恰当。

MVP模式介绍:

  1. View: 视图层,对应xml文件与Activity/Fragment
  2. Presenter: 逻辑控制层,同时持有View和Model对象
  3. Model: 实体层,负责获取实体数据

MVP模式的流程图如下:

MVP模式图解

采用MVP模式的优势是:

  1. 把业务逻辑抽离到Presenter层中,View层专注于UI的处理。
  2. 分离视图逻辑与业务逻辑,达到解耦的目的。
  3. 提高代码的阅读性。
  4. Presenter被抽象成接口,可以根据Presenter的实现方式进行单元测试。
  5. 可拓展性强。

采用MVP模式的缺点:

  1. 项目结构会对后期的开发和维护有一定的影响。具体视APP的体量而定。
  2. 代码量会增多,如何避免编写过多功能相似的重复代码是使用MVP开发的一个重点要处理的问题。
  3. 有一定的学习成本。

综上所述,在Android上采用MVP模式的优势是:大大优化代码的维护性与拓展性的同时对代码进行深度解耦,使各个层级的分工更加明晰。

Android上MVP模式的简单应用

先来看看一个简单用mvp模式模拟登陆的demo,下面的示例代码和其它简单介绍MVP模式的代码没有太大区别。如果有了解过的同学可以直接跳过看下一章关于如何优化MVP模式的结构的文章。

下面我们来看看在Android上用MVP模式实现简单的登录逻辑的方式:

. 登陆界面

登陆界面

  1. 项目的结构:

    项目结构

从上面的代码结构图可看出,用MVP模式实现登陆模块需要创建6个文件,分别是M、V、P接口文件和接口的对应实现。其中LoginActivity就是View层的具体实现。这样的好处时Activity组件只需要负责处理UI相关逻辑就可以了,而相关的业务逻辑全部抽象到Presenter层中处理。通过这种方式能够很好的避免传统Android开发中的Activity/Fragment等UI组件既负责处理UI逻辑又处理业务逻辑的结果。

. 代码实现

说了这么多,最后我们来看看代码的实现吧。

  1. ILoginModel

    1
    2
    3
    public interface ILoginModel {
    void login(String name ,String password);
    }
  2. ILoginPresenter

    1
    2
    3
    4
    5
    6
    public interface ILoginPresenter {
     
    void loginToServer(String userName,String password);
     
    void loginSucceed();
    }
  3. ILoginView

    1
    2
    3
    4
    5
    6
    public interface ILoginView {
     
    void showProgress(boolean enable);
     
    void showLoginView();
    }

上面是登陆模块对应的MVP接口的具体设计,下面我来简单介绍一下接口中的几个方法:

  • ILoginModel.login(String name ,String password)登陆方法,通过该方法向服务器发送登陆请求。
  • ILoginPresenter. loginToServer (String name ,String password)通知Model响应登陆事件。
  • ILoginPresenter. loginSucceed()当登陆事件完成时(成功/失败),Model层要通知该方法登陆事件已完成。
  • ILoginView. showProgress(boolean enable)当Presenter层调用loginToServer (String name ,String password)方法时,要通过该方法通知View层显示加载动画。
  • ILoginView. showLoginView()登陆成功时,Presenter层会通过该方法通知View层登陆已成功。

下面我们来看看这几个接口的具体实现。

  1. LoginModel

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class LoginModel implements ILoginModel{
     
    private ILoginPresenter presenter;
     
    private Handler mHandler = new Handler();
     
    public LoginModel(ILoginPresenter presenter) {
    this.presenter = presenter;
     
    }
     
    @Override
    public void login(String name ,String password) {
    mHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
    Log.d("LoginModel", "run: ");
    presenter.loginSucceed();
    }
    },2000);
    }
     
    }

上面的Model层实现了login(String name,Stringpassword)登陆方法,该方法的具体实现逻辑是通过线程休眠2秒来模拟网络登陆的过程,登陆成功后会通过LoginPresenter的loginSucceed()方法来通知Presenter层登陆结果。实际开发中我们需要根据具体的业务逻辑来实现该过程。

  1. LoginPresenter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class LoginPresenter implements ILoginPresenter{
     
    private ILoginModel loginModel;
     
    private ILoginView loginView;
     
    public LoginPresenter(ILoginView loginView) {
    this.loginView = loginView;
    this.loginModel = new LoginModel(this);
    }
     
    @Override
    public void loginToServer(String userName, String password) {
    loginView.showProgress(true);
    loginModel.login(userName,password);
    }
     
    @Override
    public void loginSucceed() {
    loginView.showProgress(false);
    loginView.showLoginView();
    }
    }

从上面代码可以看出LoginPresenter的实现逻辑很简单,首先在构造方法中获取ILoginView对象并撞见ILoginModel对象。然后当View层调用loginToServer(String userName, String password)方法成功时,通知View层显示加载动画并调用ILoginModel层的login(String userName, String password)方法向服务器发送登陆请求。当登陆成功后(即Model层通知loginSucceed方法时)通过loginView.showProgress(false)方法通知View层隐藏加载动画,并通知View登陆成功。

  1. LoginActivity
    对于LoginActivity我们只需要关注其中的几个方法即可

    1
    2
    3
    4
    5
    6
    7
    loginBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    //模拟登陆,不需要账号密码
    loginPresenter.loginToServer("","");
    }
    });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public void showProgress(boolean enable) {
if (enable){
progressBar.setVisibility(View.VISIBLE);
loginLayout.setVisibility(View.GONE);
}else {
progressBar.setVisibility(View.GONE);
loginLayout.setVisibility(View.VISIBLE);
}
}
 
@Override
public void showLoginView() {
Toast.makeText(LoginActivity.this,"登陆功",Toast.LENGTHSHORT).show();
finish();
}

上面时实现了ILoginView接口的两个方法。
结合上面的代码可以看出,当点击登陆按钮的监听事件时,我们不需要关注业务逻辑,只需要调用loginPresenter.loginToServer("","");方法即可,然后根据实际情况实现View层中ILoginView接口的方法即可,这样达到了UI业务与逻辑完全分离的目的。

Android架构篇--MVP模式的介绍篇的更多相关文章

  1. [译]Google官方关于Android架构中MVP模式的示例

    概述 该示例(TODO-MVP)是后续各种示例演变的基础,它主要演示了在不带架构性框架的情况下实现M-V-P模式.其采用手动依赖注入的方式来提供本地数据源和远程数据源仓库.异步任务通过回调处理. 注意 ...

  2. Google官方关于Android架构中MVP模式的示例续-DataBinding

    基于前面的TODO示例,使用Data Binding库来显示数据并绑定UI元素的响应动作. 这个示例并未严格遵循 Model-View-ViewModel 或 Model-View-Presenter ...

  3. 如何结合整洁架构和MVP模式提升前端开发体验 - 整体架构篇

    本文不详细介绍什么是整洁架构以及 MVP 模式,自行查看文章结尾相关链接文章. 整洁架构粗略介绍 下图为整洁架构最原始的结构图: Entities/Models:实体层,官方说法就是封装了企业里最通用 ...

  4. Android MVP模式简单易懂的介绍方式 (二)

    Android MVP模式简单易懂的介绍方式 (一) Android MVP模式简单易懂的介绍方式 (二) Android MVP模式简单易懂的介绍方式 (三) 上一篇文章我们介绍完了Model的创建 ...

  5. DDD分层架构之值对象(介绍篇)

    DDD分层架构之值对象(介绍篇) 前面介绍了DDD分层架构的实体,并完成了实体层超类型的开发,同时提供了验证方面的支持.本篇将介绍另一个重要的构造块——值对象,它是聚合中的主要成分. 如果说你已经在使 ...

  6. Android MVP模式 简单易懂的介绍方式

    主要学习这位大神的博客:简而易懂 Android MVP模式 简单易懂的介绍方式 https://segmentfault.com/a/1190000003927200

  7. Android MVP模式简单易懂的介绍方式 (三)

    Android MVP模式简单易懂的介绍方式 (一) Android MVP模式简单易懂的介绍方式 (二) Android MVP模式简单易懂的介绍方式 (三) 讲完M和P,接下来就要讲V了.View ...

  8. Android MVP模式简单易懂的介绍方式 (一)

    Android MVP模式简单易懂的介绍方式 (一) Android MVP模式简单易懂的介绍方式 (二) Android MVP模式简单易懂的介绍方式 (三) 最近正在研究Android的MVP模式 ...

  9. 浅谈Android架构之MVP,MVVM

    概述 MVP(Model-View-Presenter)是传统MVC(Model-View-Controller)在Android开发上的一种变种.进化模式.主要用来隔离UI.UI逻辑和业务逻辑.数据 ...

随机推荐

  1. sql 语句的limit的用法

    SELECT * FROM table  LIMIT [offset,] rows | rows OFFSET offset   mysql> SELECT * FROM table LIMIT ...

  2. Can't sendRedirect() after data has committed to the client

    resin下 response.sendRedirect("XXX"); 会报异常:java.lang.IllegalStateException: Can't sendRedir ...

  3. for in 循环 和for循环 for of循环

    for in 循环得到的是数组的key值 for  in 循环用以遍历对象的属性 var scores=[10,11,12]; var total=0; for(var score in scores ...

  4. Redis主从同步原理-SYNC【转】

    和MySQL主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况.为了分担读压力,Redis支持主从复制,Redis的主从结构可以采用一主多从或者级联结构,下图为级 ...

  5. Android Studio 签名打包

    项目开发完成后,如果要分发到Google play或者各个第三方渠道,签名打包是必不可少的,下面详细介绍整个签名打包过程,及如何查看签名. 1.创建签名文件 选择要打包的项目-点击Build-在弹出的 ...

  6. C#效率优化(1)-- 使用泛型时避免装箱

    本想接着上一篇详解泛型接着写一篇使用泛型时需要注意的一个性能问题,但是后来想着不如将之前的详解XX系列更正为现在的效率优化XX系列,记录在工作时遇到的一些性能优化的经验和技巧,如果有什么不足,还请大家 ...

  7. 【源码解读】EOS测试插件:txn_test_gen_plugin.cpp

    本文内容本属于<[精解]EOS TPS 多维实测>的内容,但由于在编写时篇幅过长,所以我决定将这一部分单独成文撰写,以便于理解. 关键字:eos, txn_test_gen_plugin, ...

  8. leetcode — integer-to-roman

    /** * Source : https://oj.leetcode.com/problems/integer-to-roman/ * * Created by lverpeng on 2017/7/ ...

  9. 反调试手法之CreateProcess反调试

    反调试手法之CreateProcess反调试 在学习Win32 创建进程的时候.我们发现了有一个进程信息结构体. STARTUPINFO. 这个结构体可以实现反调试. 具体CreateProcess可 ...

  10. SpringBoot---静态页面加载

    Thymeleaf模板配置: maven添加支持如下: <dependency> <groupId>org.springframework.boot</groupId&g ...