從google todo-mvp示例再次學習MVP

Ruheng發表於2019-03-02

已經使用了一段時間的MVP模式,今天再以google官方的安卓架構示例todo-mvp為例,再次學習MVP模式。

一、MVP簡介

從google todo-mvp示例再次學習MVP

  • Model: 資料層,負責與網路層和資料庫層的邏輯互動。
  • View: UI層,顯示資料, 並向Presenter報告使用者行為。
  • Presenter: 從Model拿資料,應用到UI層,管理UI的狀態,響應使用者的行為。

二、MVP優勢

在學習todo-mvp之前,先了解一下MVP的優勢。

  • 分離了檢視邏輯和業務邏輯,降低了耦合。
  • Activity只處理生命週期的任務,程式碼變得更加簡潔。
  • 檢視邏輯和業務邏輯分別抽象到了View和Presenter的介面中,提高程式碼的閱讀性。
  • Presenter被抽象成介面,可以有多種具體的實現,所以方便進行單元測試。
  • 把業務邏輯抽到Presenter中去,避免後臺執行緒引用著Activity導致Activity的資源無法被系統回收從而引起記憶體洩露和OOM。

三、todo-mvp結構解析

1.專案結構

從google todo-mvp示例再次學習MVP
從上圖可以看出,todo-mvp是按照功能模組劃分的。

其中tasks, taskdetail, addedittask, statistics是四個業務模組。

data是資料模組,其中具體的類TasksRepository擔任Model層,負責遠端和本地資料的獲取。

BasePresenterBaseView是presenter 和 view 的基類,在具體模組承擔實際功能。最後,util是工具類集合。

2.具體解析

在todoapp中,MVP的具體結構如下圖所示:

從google todo-mvp示例再次學習MVP
下面以tasks模組具體闡述上述圖片中的實際作用關係。

基類

public interface BasePresenter {

    void start();

}
複製程式碼

其中start()方法的作用是presenter開始獲取資料並呼叫view中方法改變介面顯示,其呼叫時機是在Fragment類的onResume方法中。

public interface BaseView<T> {

    void setPresenter(T presenter);

}
複製程式碼

其中setPresenter()方法作用是在將presenter例項傳入view中,其呼叫時機是presenter實現類的建構函式中

契約類

與之前使用的MVP實現不同,官方的實現中加入了契約類來統一管理view與presenter的所有的介面,這種方式使得view與presenter中有哪些功能,一目瞭然,維護起來也方便,同時使得view與presenter一一對應,並有效地減少類的數目。

public interface TasksContract {

    interface View extends BaseView<Presenter> {

        void setLoadingIndicator(boolean active);

        void showTasks(List<Task> tasks);

        void showAddTask();

        void showTaskDetailsUi(String taskId);

        void showTaskMarkedComplete();

        void showTaskMarkedActive();

        void showCompletedTasksCleared();

        void showLoadingTasksError();

        void showNoTasks();

        void showActiveFilterLabel();

        void showCompletedFilterLabel();

        void showAllFilterLabel();

        void showNoActiveTasks();

        void showNoCompletedTasks();

        void showSuccessfullySavedMessage();

        boolean isActive();

        void showFilteringPopUpMenu();
    }

    interface Presenter extends BasePresenter {

        void result(int requestCode, int resultCode);

        void loadTasks(boolean forceUpdate);

        void addNewTask();

        void openTaskDetails(@NonNull Task requestedTask);

        void completeTask(@NonNull Task completedTask);

        void activateTask(@NonNull Task activeTask);

        void clearCompletedTasks();

        void setFiltering(TasksFilterType requestType);

        TasksFilterType getFiltering();
    }
}
複製程式碼

TasksActivity

Activity 在專案中是一個全域性的控制者,負責建立 view 以及 presenter 例項,並將二者聯絡起來。

        TasksFragment tasksFragment =
                (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
        if (tasksFragment == null) {
            // 建立 fragment
            tasksFragment = TasksFragment.newInstance();
            ActivityUtils.addFragmentToActivity(
                    getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
        }

        // 建立 presenter,TasksPresenter是TasksContract.Presenter 的實現類
       // 傳入兩個引數 
       //1.TasksRepository例項,負責資料層  
       //2.tasksFragment,是TasksContract.View的實現類,即view例項
        mTasksPresenter = new TasksPresenter(
                Injection.provideTasksRepository(getApplicationContext()), tasksFragment);
複製程式碼

其中,通過例項化TasksPresenter時,傳入tasksFragment,使得在TasksPresenter中擁有view例項。同時,在例項化時初始化建構函式,呼叫了setPresenter()方法,使得view例項中擁有了presenter例項物件,使得兩者聯絡起來。

TasksPresenter建構函式如下所示:

    public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) {
        mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");
        mTasksView = checkNotNull(tasksView, "tasksView cannot be null!");

        mTasksView.setPresenter(this);
    }
複製程式碼

TasksFragment

將Fragment作為view層的實現類,使得Activity作為一個全域性控制類來建立物件,而Fragment作為view,兩者各司其職。同時,Fragment比較靈活,能夠方便的處理介面適配的問題。

public class TasksFragment extends Fragment implements TasksContract.View {
    ...........
    public static TasksFragment newInstance() {
        return new TasksFragment();
    }
    ...........
    @Override
    public void onResume() {
        super.onResume();
        mPresenter.start();
    }
    ...........
    @Override
    public void setPresenter(@NonNull TasksContract.Presenter presenter) {
        mPresenter = checkNotNull(presenter);
    }
    ...........
    @Override
    public boolean isActive() {
        return isAdded();
    }
    ...........
}
複製程式碼

對於 view 的實現TasksFragment,只挑一部分重要的方法來看。

  • newInstance ()方法,例項化TasksFragment物件。
  • setPresenter()方法繼承於父類,通過該方法,view 獲得了 presenter 得例項,從而可以呼叫 presenter 程式碼來處理業務邏輯。
  • onResume()中,呼叫了 presenter 得start()方法,獲取資料並操作view介面的顯示。
  • isActive()方法,通過isAdded()判斷對應Activity是否銷燬。在Fragment在執行非同步耗時操作後,如果呼叫Activity例項,應當先使用isActive()方法加以判斷。

四、總結

通過對todo-mvp分析,再次瞭解學習了MVP。從google提供的例子中可以看出,MVP的實現較為簡單,model、view和presenter各個職責明確,便於擴充套件維護。contract契約類的出現,使得model和presenter結構更加清晰明瞭。Activity和Fragment的配合,使得Activity職能更為簡化,同時View的實現更加靈活。

相關文章