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閱讀 227,428評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,695評論 2 370

推薦閱讀更多精彩內容