1. 程式人生 > >Android架構篇--MVP模式的介紹篇

Android架構篇--MVP模式的介紹篇

跳過 fan string btn 開始 tex 維護 服務器 run

摘要: 在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模式的介紹篇