Android開發模式:模型—視圖—主導器模式

我們有可能使用過MVC(Model—View—Controller)模式,但當我們用更優的方法測試Android代碼時,使用MVP(模型—視圖—主導器:Model—View—Presenter)模式可能更合適。MVP模式與MVC模式的根本區別是:在MVP模式中,視圖中的業務邏輯被放入主導器中,主導器通過接口與視圖交互。在MVC模式中,視圖可以包含訪問模型的邏輯。在MVP模式中,視圖與模型是隔離的,所有與視圖和模型的交互操作都是在主導器中完成,因此主導器在整個MVP模式中處於“主導”地位。

在接下來的demo裏,我會展示如何在Android中使用MVP模式,以及如何利用該模式提高代碼的易測性。爲了演示其運行機制,我們創建一個啓動畫面。所謂啓動畫面,就是一個普通的界面,在應用程序開始運行前做一些初始化和驗證工作。在本例中,我們會在啓動畫面中檢查網絡連接是否正常,並顯示一個進度條。如果網絡連接正常,就切換到另一個Activity中;否則便不會切換到其他Activity,而是向用戶顯示一條錯誤信息以阻止程序繼續運行。

要創建啓動畫面,需要一個負責在模型和視圖間交互的主導器。在本例中,主導器有兩個功能:一個功能用於判斷網絡是否連接,另一個功能用於控制視圖。

主導器中會用到一個模型類ConnectionStatus,該類實現了IConnectionStatus接口,該接口中只定義了一個判斷網絡是否在線的方法。源碼如下所示:

public interface IConnectionStatus {
  boolean isOnline();
}

負責控制視圖的代碼位於Activity中,並且這個Activity實現了ISplashView接口。主導器會通過該接口控制應用程序的執行過程。ISplashView接口的源碼如下所示:

public interface ISplashView {

  void showProgress();
  void hideProgress();
  void showNoInetErrorMsg();
  void moveToMainView();
}

因爲我們是在Android平臺上開發應用程序,因此首先需要創建視圖,然後我們會把視圖的控制權交給主導器。代碼如下所示:

public class SplashActivity extends Activity implements ISplashView {

  private TextView mTextView;
  private ProgressBar mProgressBar;
  private SplashPresenter mPresenter;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.splash);

    //爲當前Activity初始化主導器,並將當前Activity設置給主導器
    mPresenter = new SplashPresenter();
    mPresenter.setView(this);

    //Activity初始化代碼
    mTextView = (TextView) findViewById(R.id.splash_text);
    mProgressBar = (ProgressBar) findViewById(R.id.splash_progress_bar);
  }

  @Override
  protected void onResume() {
    super.onResume();
    //運行onResume()方法時,通知主導器當前視圖已經準備完畢,可以把控制權交給主導器了
    mPresenter.didFinishLoading();
  }

  @Override
  public void showProgress() {
    mProgressBar.setVisibility(View.VISIBLE);
  }

  @Override
  public void hideProgress() {
    mProgressBar.setVisibility(View.INVISIBLE);
  }

  @Override
  public void showNoInetErrorMsg() {
    mTextView.setText("No internet");
  }

  @Override
  public void moveToMainView() {
    startActivity(new Intent(this, MainActivity.class));
  }
}

主導器的代碼比較簡單,其源碼如下所示:

public class SplashPresenter {

  private IConnectionStatus mConnectionStatus;
  private ISplashView mView;

  public SplashPresenter() {
    this(new ConnectionStatus());
  }

  public SplashPresenter(IConnectionStatus connectionStatus) {
    mConnectionStatus = connectionStatus;
  }

  public void setView(ISplashView view) {
    this.mView = view;
  }

  protected ISplashView getView() {
    return mView;
  }

  public void didFinishLoading() {
    //獲取視圖,即設置給主導器的ISplashView接口的實現類的引用
    ISplashView view = getView();

    //判斷程序是否繼續執行的邏輯
    if (mConnectionStatus.isOnline()) {
      view.moveToMainView();
    } else {
      view.hideProgress();
      view.showNoInetErrorMsg();
    }
  }
}

從上述代碼可以看出,主導器通過接口訪問視圖,主導器並不知道該接口是由Activity實現的。這樣,在單元測試中就更容易模擬(mock)視圖。

MVP模式可以使代碼更易組織且更易測試。在上述demo中,有一個測試文件夾,在測試代碼中需要初始化主導器並模擬(mock)接口。在主導器中並未使用任何Android平臺相關的代碼,因此不需要在Android設備生運行測試用例,只需要在JVM上運行即可。此外,在本例中我們使用Mockito模擬接口。

在Android平臺上開發應用程序,我們會發現Activity中會存在大量代碼。遺憾的是,測試Activity是很痛苦的。使用MVP模式不僅可以簡化創建測試用例的過程,還可以更容易地實施TDD(test-driven development,測試驅動開發)。

代碼地址

發佈了86 篇原創文章 · 獲贊 6 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章