安卓常見架構之——mvp

綜述

  對於MVP (Model View Presenter)架構是從著名的MVC(Model View Controller)架構演變而來的。而對於Android應用的開發中自己可視爲一種MVC架構。一般在開發中將XML文件視爲MVC中的View角色,而將Activity則視爲MVC中的Controller角色。不過更多狀況下在實際應用開發中Activity不可以徹底充當Controller,而是Controller和View的合體。因而Activity既要負責視圖的顯示,又要負責對業務邏輯的處理。這樣在Activity中代碼達到上千行,甚至幾千行都不足爲其,同時這樣的Activity也顯得臃腫不堪。因此對於MVC架構並不很合適運用於Android的開發中。下面就來介紹一下MVP架構以及看一下google官方給出的MVP架構示例。前端

MVP架構簡介

  對於一個應用而言咱們須要對它抽象出各個層面,而在MVP架構中它將UI界面和數據進行隔離,因此咱們的應用也就分爲三個層次。java

  • View: 對於View層也是視圖層,在View層中只負責對數據的展現,提供友好的界面與用戶進行交互。在Android開發中一般將Activity或者Fragment做爲View層。
  • Model: 對於Model層也是數據層。它區別於MVC架構中的Model,在這裏不只僅只是數據模型。在MVP架構中Model它負責對數據的存取操做,例如對數據庫的讀寫,網絡的數據的請求等。
  • Presenter:對於Presenter層他是鏈接View層與Model層的橋樑並對業務邏輯進行處理。在MVP架構中Model與View沒法直接進行交互。因此在Presenter層它會從Model層得到所須要的數據,進行一些適當的處理後交由View層進行顯示。這樣經過Presenter將View與Model進行隔離,使得View和Model之間不存在耦合,同時也將業務邏輯從View中抽離。android

      下面經過MVP結構圖來看一下MVP中各個層次之間的關係。 
    這裏寫圖片描述
      在MVP架構中將這三層分別抽象到各自的接口當中。經過接口將層次之間進行隔離,而Presenter對View和Model的相互依賴也是依賴於各自的接口。這點符合了接口隔離原則,也正是面向接口編程。在Presenter層中包含了一個View接口,而且依賴於Model接口,從而將Model層與View層聯繫在一塊兒。而對於View層會持有一個Presenter成員變量而且只保留對Presenter接口的調用,具體業務邏輯所有交由Presenter接口實現類中處理。git

官方MVP架構分析

項目介紹

  對於MVP架構有了一些的瞭解,而在前端時間Google給出了一些App開發架構的實現。 
  項目地址爲:https://github.com/googlesamples/android-architecture
  在這裏進入README看一下此次Google給出那些Android開發架構的實現。github

這裏寫圖片描述
  對於上面五個開發架構的實現表示到目前爲止是已經完成的項目,而下面兩個則表示正在進行的中的項目。如今首先來介紹一下這幾個架構。數據庫

    • todo-mvp: 基礎的MVP架構。
    • todo-mvp-loaders:基於MVP架構的實現,在獲取數據的部分採用了loaders架構。
    • todo-mvp-databinding: 基於MVP架構的實現,採用了數據綁定組件。
    • todo-mvp-clean: 基於MVP架構的clean架構的實現。
    • todo-mvp-dagger2: 基於MVP架構,採用了依賴注入dagger2。
    • dev-todo-mvp-contentproviders: 基於mvp-loaders架構,使用了ContenPproviders。
    • dev-todo-mvp-rxjava: 基於MVP架構,對於程序的併發處理和數據層(MVP中的Model)的抽象。編程

        從上述的介紹中能夠看出,對於官方給出全部的架構的實現最終都是基於MVP架構。因此在這裏就對上面的基礎的MVP架構todo-mvp進行分析。網絡

      項目結構的分析

        對於這個項目,它實現的是一個備忘錄的功能。對於工做中未完成的任務添加到待辦任務列表中。咱們可以在列表中能夠對已完成的任務作出標記,可以進入任務詳細頁面修改任務內容,也可以對已完成的任務和未完成的任務數量作出統計。 
        首先在這裏來看一下todo-mvp總體的項目結構 
      架構

        從上圖中能夠看出,項目總體包含了一個app src目錄,四個測試目錄。在src目錄下面對代碼的組織方式是按照功能進行劃分。在這個項目中包含了四個功能,它們分別是:任務的添加編輯(addedittask),任務完成狀況的統計(statistics),任務的詳情(taskdetail),任務列表的顯示(tasks)。對於data包它是項目中的數據源,執行數據庫的讀寫,網絡的請求操做都存放在該包內,也是MVP架構中的Model層。而util包下面則是存放一些項目中使用到的工具類。在最外層存放了兩接口BasePresenter和BaseView。它們是Presenter層接口和View層接口的基類,項目中全部的Presenter接口和View層接口都繼承自這兩個接口。 
        如今進入功能模塊內看下在模塊內部對類是如何劃分的。在每一個功能模塊下面將類分做xxActivity,xxFragment,xxPresenter,xxContract。也正是這些類構成了項目中的Presenter層與View層。下面就來分析在這個項目中是如何實現MVP架構。併發

      MVP架構的實現

        在這裏只從宏觀上關注MVP架構的實現,對於代碼的內部細節在就不在具體分析。那麼就以任務的添加和編輯這個功能來看一下Android官方是如何實現MVP架構。

      Model層的實現

        首先咱們從MVP架構的最內層開始分析,也就是對應的Model層。在這個項目中對應的data包下的內容。在data下對數據庫等一些數據源的封裝。對於Presenter層提供了TasksDataSource接口。在這裏看一下這個TasksDataSource接口。

    • public interface TasksDataSource {
      
          interface LoadTasksCallback {
      
              void onTasksLoaded(List<Task> tasks);
      
              void onDataNotAvailable();
          }
      
          interface GetTaskCallback {
      
              void onTaskLoaded(Task task);
      
              void onDataNotAvailable();
          }
      
          void getTasks(@NonNull LoadTasksCallback callback);
      
          void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);
      
          void saveTask(@NonNull Task task);
      
          ......
      }
      

        TasksDataSource接口的實現是TasksLocalDataSource,在TasksDataSource中的方法也就是一些對數據庫的增刪改查的操做。而在TasksDataSource的兩個內部接口LoadTasksCallback和GetTaskCallback是Model層的回調接口。它們的真正實現是在Presenter層。對於成功獲取到數據後變或經過這個回調接口將數據傳遞Presenter層。一樣,如果獲取失敗一樣也會經過回調接口來通知Presenter層。下面來看一下TasksDataSource的實現類。

    • public class TasksLocalDataSource implements TasksDataSource {
      
          ......
      
          @Override
          public void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback) {
      
              //根據taskId查訓出相對應的task
              ......
      
              if (task != null) {
                  callback.onTaskLoaded(task);
              } else {
                  callback.onDataNotAvailable();
              }
          }
      
          @Override
          public void saveTask(@NonNull Task task) {
              checkNotNull(task);
              SQLiteDatabase db = mDbHelper.getWritableDatabase();
      
              ContentValues values = new ContentValues();
              values.put(TaskEntry.COLUMN_NAME_ENTRY_ID, task.getId());
              values.put(TaskEntry.COLUMN_NAME_TITLE, task.getTitle());
              values.put(TaskEntry.COLUMN_NAME_DESCRIPTION, task.getDescription());
              values.put(TaskEntry.COLUMN_NAME_COMPLETED, task.isCompleted());
      
              db.insert(TaskEntry.TABLE_NAME, null, values);
      
              db.close();
          }
      
          ......
      }
      

        

      在這裏咱們針對任務的添加和編輯功能,因此省略不少代碼。能夠看出在TasksLocalDataSource中實現的getTask方法,在這個方法中傳入TasksDataSource內的GetTaskCallback回調接口。在getTask方法的實現能夠看出在查詢到Task之後調用回調方法,如果在Presenter層中實現了這兩個回調方法,便將數據傳遞到Presenter層。而對於查詢到的Task爲空的時候也是經過回調方法執行對應的操做。 
        一樣對於經過網絡請求獲取到數據也是同樣,對於成功請求到的數據能夠經過回調方法將數據傳遞到Presenter層,對於網絡請求失敗也可以經過回調方法來執行相對應的操做。

      Presenter與View層提供的接口

        因爲在Presenter和View層所提供的接口在一個類中,在這裏就先來查看他們對外所提供了哪些接口。首先觀察一下兩個基類接口BasePresenter和BaseView。

      BasePresenter

    • package com.example.android.architecture.blueprints.todoapp;
      
      public interface BasePresenter {
      
          void start();
      
      }
      

        

      在BasePresenter中只存在一個start方法。這個方法通常所執行的任務是在Presenter中從Model層獲取數據,並調用View接口顯示。這個方法通常是在Fragment中的onResume方法中調用。

      BaseView

    • package com.example.android.architecture.blueprints.todoapp;
      
      public interface BaseView<T> {
      
          void setPresenter(T presenter);
      
      }
      

        

       在BaseView中只有一個setPresenter方法,對於View層會存在一個Presenter對象。而setPresenter正是對View中的Presenter進行初始化。

      AddEditTaskContract

        在Android官方給出的MVP架構當中對於Presenter接口和View接口提供的形式與咱們平時在網上所見的有所不一樣。在這裏將Presenter中的接口和View的接口都放在了AddEditTaskContract類裏面。這樣一來咱們可以更清晰的看到在Presenter層和View層中有哪些功能,方便咱們之後的維護。下面就來看一下這個AddEditTaskContract類。

    • public interface AddEditTaskContract {
      
          interface View extends BaseView<Presenter> {
      
              void showEmptyTaskError();
      
              void showTasksList();
      
              void setTitle(String title);
      
              void setDescription(String description);
      
              boolean isActive();
          }
      
          interface Presenter extends BasePresenter {
      
              void createTask(String title, String description);
      
              void updateTask( String title, String description);
      
              void populateTask();
          }
      }
      

        

      在這裏很清晰的能夠看出在View層中處理了一些數據顯示的操做,而在Presenter層中則是對Task保存,更新等操做。

      Presenter層的實現

        下面就來看一下在Presenter是如何實現的。

    • public class AddEditTaskPresenter implements AddEditTaskContract.Presenter,
              TasksDataSource.GetTaskCallback {
      
          ......
      
          public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository,
                  @NonNull AddEditTaskContract.View addTaskView) {
              mTaskId = taskId;
              mTasksRepository = checkNotNull(tasksRepository);
              mAddTaskView = checkNotNull(addTaskView);
      
              mAddTaskView.setPresenter(this);
          }
      
          @Override
          public void start() {
              if (mTaskId != null) {
                  populateTask();
              }
          }
      
          ......
      
          @Override
          public void populateTask() {
              if (mTaskId == null) {
                  throw new RuntimeException("populateTask() was called but task is new.");
              }
              mTasksRepository.getTask(mTaskId, this);
          }
      
          @Override
          public void onTaskLoaded(Task task) {
              // The view may not be able to handle UI updates anymore
              if (mAddTaskView.isActive()) {
                  mAddTaskView.setTitle(task.getTitle());
                  mAddTaskView.setDescription(task.getDescription());
              }
          }
          ......
      }
      

        

       在這裏能夠看到在AddEditTaskPresenter中它不只實現了本身的Presenter接口,也實現了GetTaskCallback的回調接口。而且在Presenter中包含了Model層TasksDataSource的對象mTasksRepository和View層AddEditTaskContract.View的對象mAddTaskView。因而整個業務邏輯的處理就擔負在Presenter的身上。 
        從Presenter的業務處理中能夠看出,首先調用Model層的接口getTask方法,經過TaskId來查詢Task。在查詢到Task之後,因爲在Presenter層中實現了Model層的回調接口GetTaskCallback。這時候在Presenter層中就經過onTaskLoaded方法獲取到Task對象,最後經過調用View層接口實現了數據的展現。

      View層的實現

        對於View的實現是在Fragment中,而在Activity中則是完成對Fragment的添加,Presenter的建立操做。下面首先來看一下AddEditTaskActivity類。

    • public class AddEditTaskActivity extends AppCompatActivity {
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.addtask_act);
      
              ......
      
              if (addEditTaskFragment == null) {
                  addEditTaskFragment = AddEditTaskFragment.newInstance();
                  ......
                  ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
                          addEditTaskFragment, R.id.contentFrame);
              }
      
              // Create the presenter
              new AddEditTaskPresenter(
                      taskId,
                      Injection.provideTasksRepository(getApplicationContext()),
                      addEditTaskFragment);
          }
      
          ......
      }
      

        對於Activity的提供的功能也是很是的簡單,首先建立Fragment對象並將其添加到Activity當中。以後建立Presenter對象,並將Fragment也就是View傳遞到Presenter中。 
        下面再來看一下View的實現,也就是Fragment。

    • public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View {
      
          ......
      
          @Override
          public void onResume() {
              super.onResume();
              mPresenter.start();
          }
      
          @Override
          public void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) {
              mPresenter = checkNotNull(presenter);
          }
      
          ......
      
          @Override
          public void setTitle(String title) {
              mTitle.setText(title);
          }
      
          ......
      }

      在這對於源碼就不在過多貼出。在Fragment中,經過setPresenter獲取到Presenter對象。並經過調用Presenter中的方法來實現業務的處理。而在Fragment中則只是對UI的一些操做。這樣一來對於Fragment類型的代碼減小了不少,而且邏輯更加清晰。 
        咱們注意到View層的實現是經過Fragment來完成的。對於View的實現爲何要採用Fragment而不是Activity。來看一下官方是如何解釋的。

    •  The separation between Activity and Fragment fits nicely with this implementation of MVP: the Activity is the overall controller that creates and connects views and presenters.
      Tablet layout or screens with multiple views take advantage of the Fragments framework.
      

        

      在這裏官方對於採用Fragment的緣由給出了兩種解釋。

      • 經過Activity和Fragment分離很是適合對於MVP架構的實現。在這裏將Activity做爲全局的控制者將Presenter於View聯繫在一塊兒。
      • 採用Fragment更有利於平板電腦的佈局或者是多視圖屏幕。

      總結

        經過MVP架構的使用能夠看出對於各個層次之間的職責更加單一清晰,同時也很大程度上下降了代碼的耦合度。對於官方MVP架構示例,google也明確代表對於他們所給出的這些架構示例只是做爲參考,而不是一個標準。因此對於基礎的MVP架構有更大的擴展空間。例如綜合google給出的示例。咱們能夠經過在MVP架構的基礎上使用dagger2,rxJava等來構建一個Clean架構。也是一個很好的選擇。

  • 聲明:此文章轉載http://blog.csdn.net/ljd2038/article/details/51477475
相關文章
相關標籤/搜索