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

摘要: 在MVVM成熟以前MVP模式在Android上有被神化的趨勢,筆者曾經在商業項目中從零開始大規模採用過MVP模式對項目進行開發。在使用MVP模式進行開發的時候發現項目的結構模式對開發是有必定的影響的,在這裏筆者會對這一問題進行探討。但願經過這篇blog能讓讀者瞭解如何使用MVP模式搭建一個功能完善的MVP模式開發框架,避免一些筆者認爲比較嚴重的問題。git

爲何要使用MVP模式

在傳統的Android開發中,咱們通常是使用MVC模式進行開發的。
傳統MVC模式介紹:github

  1. View: 視圖層,對應xml文件
  2. Controller: 控制層,對應Activity和Fragment層,進行數據處理
  3. Model:實體層,負責獲取實體數據

在Android開發中採用MVC模式的一個最大的弊端就是xml做爲View層視圖能力實在太弱,因此通常狀況下咱們都是經過Controller層來輔助處理一些視圖的。這樣的結果就致使Controller既做爲控制層的同時又承擔了View層的大部分功能,採用MVC模式每每會致使Activity和Fragment中的代碼很是複雜。咱們將Android中採用的MVC模式稱爲MV模式更加恰當。web

MVP模式介紹:服務器

  1. View: 視圖層,對應xml文件與Activity/Fragment
  2. Presenter: 邏輯控制層,同時持有View和Model對象
  3. Model: 實體層,負責獲取實體數據

MVP模式的流程圖以下:
網絡

MVP模式圖解框架

採用MVP模式的優點是:ide

  1. 把業務邏輯抽離到Presenter層中,View層專一於UI的處理。
  2. 分離視圖邏輯與業務邏輯,達到解耦的目的。
  3. 提升代碼的閱讀性。
  4. Presenter被抽象成接口,能夠根據Presenter的實現方式進行單元測試。
  5. 可拓展性強。

採用MVP模式的缺點:post

  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業務與邏輯徹底分離的目的。

相關文章
相關標籤/搜索