Carson带你学Android:一文带你全面了解MVC、MVP、MVVM模式(含实例讲解)


前言

  • Android开发中,当你梳理完需求后,你要做的并不是马上写下你的第一行代码,而是需先设计好整个项目的技术框架
  • 今天,我将全面介绍Android开发中主流的技术框架MVCMVPMVVM模式,并实例讲解MVP模式,希望您们会喜欢。

目录

示意图

1. 为什么要进行技术框架的设计

  • 模块化功能
    使得程序模块化,即:内部的高聚合模块之间的低耦合
  • 提高开发效率
    开发人员只需专注于某一点(视图显示、业务逻辑 / 数据处理)
  • 提高测试效率
    方便后续的测试 & 定位问题

切记:不要为了设计而设计,否则反而会提高开发量

示意图


2. Android开发主流的技术框架

  • 主要有MVCMVPMVVM 3种模式
  • 下面,我将详细 & 具体的介绍上述3种模式

2.1 MVC模式

  • 角色说明
示意图
  • 模式说明
示意图
  • 该模式存在的问题:Activity责任不明、十分臃肿
    Activity由于其生命周期的功能,除了担任View层的部分职责(加载应用的布局、接受用户操作),还要承担Controller层的职责(业务逻辑的处理)
    随着界面的增多 & 逻辑复杂度提高,Activity类的代码量不断增加,越加臃肿

2.2 MVP模式

  • 出现的原因
    为了解决上述MVC模式存在的问题,把分离Activity中的View层 和 Controller层的职责,从而对Activity代码量进行优化、瘦身,所以出现了MVP模式

  • 角色说明

示意图
  • 模式说明
示意图
  • 优点:(对比MVC模式)
  1. 耦合度更低:通过Presenter实现数据和视图之间的交互,完全隔离了View层与Mode层,二者互不干涉

避免了ViewModel的直接联系,又通过Presenter实现两者之间的沟通

  1. Activity代码变得更加简洁:简化了Activity的职责,仅负责UI相关操作,其余复杂的逻辑代码提取到了Presenter层中进行处理

2.3 MVVM

为了更加分离M、V层,更加释放Activity的压力,于是出现了MVVM模式

  • 定义
    VM层:ViewModel,即 View的数据模型和Presenter的合体

基本上与 MVP 模式完全一致,将逻辑处理层 Presenter 改名为 ViewModel

  • 模式说明
示意图
  • 优点
    使得视图层(View)& 控制层(Controller)之间的耦合程度进一步降低,关注点分离更为彻底,同时减轻了Activity的压力

本文主要讲解MVC和MVP模式,不过多阐述MVVM模式.


3. MVC、MVP模式的区别

示意图

4. 三种模式出现的初衷

  • MVC模式的出现
    为解决程序模块化问题,于是MVC模式出现了:将业务逻辑、数据处理与界面显示进行分离来组织代码,即分成M、V、C层;
  • MVP模式的出现
    但M、V层还是有相互交叉、隔离度不够,同时写到Activity上使得Activity代码臃肿,于是出现了MVP: 隔离了MVC中的 M 与 V 的直接联系,将M、V层更加隔离开来,并释放了Activity的压力;
  • MVVM模式的出现
    为了更加分离M、V层,更加释放Activity的压力,于是出现了MVVM: 使得V和M层之间的耦合程度进一步降低,分离更为彻底,同时更加减轻了Activity的压力。

下面,我将详细讲解一下最常用的MVP模式的核心思想 & 使用


5. MVP模式详解

此处主要详细分析MVP模式的核心思想,并实例说明。

5.1 核心思想

把Activity里的逻辑都抽离到ViewPresenter接口中去 & 由具体的实现类来完成。具体实现思路如下:

  1. Activity中的UI逻辑抽象成View接口 & 由具体的实现类来完成
  2. 把业务逻辑抽象成Presenter接口 & 由具体的实现类来完成
  3. Model类还是原来MVC模式的Model

5.2 实现步骤

MVP模式的UML

示意图

通过UML图可看出,使用MVP模式的步骤如下:

示意图

5.3 实例讲解

本节通过一个 英语词典app实例 讲解 MVP模式具体的实现

前言:工程项目的列表架构

MVP技术架构的项目结构非常清晰:把MVP层分别分为三个文件夹:ModelViewPresenter,每个文件下分别是对应的接口和实现的类

其中Model层的fanyi类是作为实现用GSON解析JSON信息的一个JavaBean

步骤1:设置View层(IView接口 & 实现类)

/**
  * View接口:IfanyiView
  * 需定义在实现类中需要用到的方法
  */

  public interface IfanyiView {    

    void init();//初始化   
    void SetInfo(String str); //输出翻译信息    
    void SetError(); //输出出错信息

    }

/**
  * View实现类:MainActivity类
  * 注:由于MainActivity是对应View层的实现类,所以要实现View层的接口
  */

  public class MainActivity extends AppCompatActivity implements IfanyiView {   

      private EditText et;    
      private TextView tv;    
      CidianPresenter cidianPresenter;  // 声明了Presenter对应类 

        @Override    
        protected void onCreate(Bundle savedInstanceState) { 
               super.onCreate(savedInstanceState); 
               setContentView(R.layout.activity_main);        
                  // 实例化P对应类的对象和findView        
                   init();        
                // 接受用户的输入  
                findViewById(R.id.btnfanyi).setOnClickListener(new View.OnClickListener() {      
              
                    @Override            
                    public void onClick(View v) {                
                    //将View层获得的数据传入Presenter层 ,注意还要传递MainActivity
                          cidianPresenter.InputToModel(et.getText().toString(), MainActivity.this);            
                         }        
                      });    
                    }    

                    @Override    
                    public void init(){        
                    //实例化P类的对象和findView        
                      cidianPresenter = new CidianPresenter(this);        
                      et = (EditText) findViewById(R.id.editText);        
                      tv = (TextView) findViewById(R.id.tv);    
                    }

                    @Override  
                    //输出出错信息   
                    public void SetError() {        
                      tv.setText("查询不成功,请检查网络");    
                     }

                    //输出翻译信息
                    @Override    
                    public void SetInfo(String str){        
                    tv.setText(str);    
                     }
                    }

  // 从上述代码可看出,MainActivity只做了FindView、setListener的工作(包含了cidianPresenter),简洁清爽!

步骤2:设置Presenter层(创建IPresenter接口&实现类)

/**
  * Presenter接口:ICidianPresenter
  * 需定义在实现类中需要用到的方法
  */

  public interface ICidianPresenter {    
      
     void InputToModel(String input,Context context); // 将View层获得的数据传入Model层

  }


/**
  * Presenter层的实现类:CidianPresenter类
  * 注:由于CidianPresenter是对应Presenter层的实现类,所以要实现Presenter层的接口
  */

  public class CidianPresenter implements onfanyiListener,ICidianPresenter {    
      // 1. 声明View层对应接口、Model层对应的类    
      IfanyiView fyV;    
      fanyimodel fanyimodel;    

      // 2. 重构函数,初始化View接口实例、Model实例    
      public  CidianPresenter(IfanyiView fyV){        
          this.fyV = fyV;        
          fanyimodel = new fanyimodel();   
       }  

      // 3.将View层获得的数据传入Model层,注意要传递this.当前类
          @Override    
          public void InputToModel(String input, Context context){  

          fanyimodel.HandleData(input, context, this);    

          }    
          // 回调函数,调用UI更新  
          @Override    
          public void onSuccess(String str) {        
              fyV.SetInfo(str);    }  
          // 回调函数,调用UI输出出错信息
          @Override    
          public void onError() {        
              fyV.SetError();    } 
          }
 
      // 注:
      // a. 保留IfanyiView的引用,就可直接在CidianPresenter当前类进行UI操作而不用在Activity操作
      // b. 保留了Model层的引用就可以将View层的数据传递到Model层

步骤3:Model层(Model层接口 & 实现类)

/**
  * Model层接口:Ifanyi
  * 需定义在实现类中需要用到的方法
  */
  public interface Ifanyi {  

    void HandleData(String input,Context context,final onfanyiListener listener);    
    String fanyiToString(fanyi fy);

  }

/**
  * Model层的实现类:fanyiModel类
  * 注:由于fanyiModel是对应Model层的实现类,所以要实现Model层的接口
  */

  public class fanyimodel implements Ifanyi {

      private fanyi fy = new fanyi();

      public void HandleData(String input,Context context,final onfanyiListener listener){

          // 使用Volley框架来实现异步从网络的有道API获取翻译数据
          RequestQueue mQueue = Volley.newRequestQueue(context);
          StringRequest stringRequest = new StringRequest("http://fanyi.youdao.com/openapi.do?keyfrom=Yanzhikai&key=2032414398&type=data&doctype=json&version=1.1&q="+input, new Response.Listener<String>() {
              @Override
              public void onResponse(String s) {

                  // 用Gson方式解析获得的json字符串
                  Gson gson = new Gson();
                  fy = gson.fromJson(s.trim(),fy.getClass());

                  // 回调监听器的函数把处理数据后的结果(翻译结果)返回给Presenter层
                  listener.onSuccess(fanyiToString(fy));
              }
          }, new Response.ErrorListener() {
              @Override
              public void onErrorResponse(VolleyError volleyError) {
                  listener.onError();
              }
          });
          mQueue.add(stringRequest);
      }

      public String fanyiToString(fanyi fy){
          // 处理解析后的json数据,转成UI输出的字符串
          String strexplain = "解释:";
          String strphonetic = "发音:";
          String strweb = "网络释义:";
          if (fy.basic == null){return "你所查找的还没有准确翻译";}
          for (int i = 0; i<fy.basic.explains.length; i++){
              strexplain +=fy.basic.explains[i]+"\n";
              if (i != fy.basic.explains.length-1 )
              {strexplain +="\t\t\t\t";}
          }
          strphonetic += fy.basic.phonetic +"\n";
          for (int i = 0; i<fy.web.size(); i++){
              for(int j = 0; j<fy.web.get(i).value.length;j++)
              {
                  strweb += fy.web.get(i).value[j]+",";
              }
              strweb += fy.web.get(i).key+"\n";
              strweb += "\t\t\t\t\t\t\t";
          }
          return strexplain+"\n"+strphonetic+"\n"+strweb;
      }
  }

至此,关于MVP模式的实例讲解,讲解完毕。


6. 总结

  • 本文主要讲解了Android开发中主流的技术框架MVCMVPMVVM模式
  • 下面我将继续对 Android中的知识进行深入讲解 ,有兴趣可以继续关注Carson_Ho的安卓开发笔记

相关系列文章阅读
Carson带你学Android:学习方法
Carson带你学Android:四大组件
Carson带你学Android:自定义View
Carson带你学Android:异步-多线程
Carson带你学Android:性能优化
Carson带你学Android:动画


欢迎关注Carson_Ho的简书

不定期分享关于安卓开发的干货,追求短、平、快,但却不缺深度


请点赞!因为你的鼓励是我写作的最大动力!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容