網上關於MVP的博客不少,本筆記做爲學習google提供的MVP的學習筆記,html
谷歌開發中國中文內站:https://developers.google.cn/java
在網站上找到Android,點擊進入後能夠找到一些開發的說明幫助,谷歌也提供了一些開發模板,瀏覽示例代碼裏,能夠找到 Google Samples page on GitHub。裏面提供了一些開發demo,例如本文裏的MVP開發模式就能夠從找android-architecture 找詳細的demo。android
網上也有不少對於官方MVP demo解讀的文章。例如這篇文章就有比較詳細的說明http://www.cnblogs.com/mengdd/p/5988104.htmlgit
如下是MVC的框架圖:github
如下是MVP的框架圖:app
借用網上的圖對於MVC與MVP的區別, 在MVC框架中,View是能夠直接讀取Model模型中的數據的,Model模型數據發生改變是會通知View數據顯示發生相應的改變,致使View既須要負責視圖的顯示和數據的處理。而在MVP中Model和View之間的沒有任何聯繫,是兩個徹底獨立的模塊,當Model模型發生數據改變時,經過Presenter通知View視圖發生相應的UI改變,View層只須要接受 Presenter的數據,並在視圖上作相應的改變。框架
Sample | Description | |
---|---|---|
todo‑mvp | Demonstrates a basic Model‑View‑Presenter (MVP) architecture and provides a foundation on which the other samples are built. This sample also acts as a reference point for comparing and contrasting the other samples in this project. | |
todo‑mvp‑loaders | Fetches data using the Loaders API. | |
todo‑databinding | Uses the Data Binding Library. | |
todo‑mvp‑clean | Uses concepts from Clean Architecture. | |
todo‑mvp‑dagger | Uses Dagger2 to add support for dependency injection. | |
todo‑mvp‑contentproviders | Based on the todo-mvp-loaders sample, this version fetches data using the Loaders API, and also makes use of content providers. | |
todo‑mvp‑rxjava | Uses RxJava to implement concurrency, and abstract the data layer. |
Sample | Description |
---|---|
dev‑todo‑mvp‑tablet | Adds a master and detail view for tablets. |
dev‑todo‑mvvm‑databinding | Based on the todo-databinding sample, this version incorporates the Model‑View‑ViewModel architecture. |
For information about planned samples, see "New sample" issues.less
External samples are variants that may not be in sync with the rest of the branches in this repository.mvvm
Sample | Description |
---|---|
todo‑mvp‑fragmentless | Uses View objects instead of Fragment objects. |
todo‑mvp‑conductor | Uses the Conductor framework to refactor the app to use a single Activity architecture. |
官方中對MVP有一個比較詳細的demo代碼,一個完整的數據增刪改查的過程。ide
共有11個完整的示例,從簡單到複雜,一步一步的說明MVP如何的工做。
先看第一個todo‑mvp工程,demo分四個界面,
1.列表頁,具體功能有A:顯示所有Task列表,B:顯示狀態爲Active狀態的Task列表,C:顯示狀態爲Completed狀態的Task列表 ,D:刷新列表,E:清除Completed狀態的數據。
2.詳情頁,修改頁,兩個頁面是同一個,能夠從詳情狀態變成修改狀態,並有刪功能。
3.建立頁,建立一個task,
4.統計頁 ,統計Active狀態的Task和Completed狀態的Task有多少個。
先分析列表頁的View, Presenter和Data三層接口
Data層接口
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);//獲取Tasks列表 void saveTask(@NonNull Task task);//保存一個tasks void completeTask(@NonNull Task task);//把一個task標記爲completed void clearCompletedTasks();//清除completed狀態的task void deleteTask(@NonNull String taskId);//刪除一個task void refreshTasks();//刷新列表。 一系列的增刪改查的方法....省略,具體能夠查看官方提供的完整demo. }
TasksDataSource 的子類除了TasksLocalDataSource,TasksRemoteDataSource外,還有一個TasksRepository,TasksRepository組合了TasksLocalDataSource,TasksRemoteDataSource兩個實例,TasksRepository負責與Presenter層交互,做爲P層與Data層交互的橋樑, TasksLocalDataSource,TasksRemoteDataSource 並不直接與Presenter層交互。TasksRepository是一個單例。
Presenter與View層
這兩層都寫在一個叫TasksContract的接口類中
public interface BaseView<T> { void setPresenter(T presenter); }
public interface TasksContract { interface View extends BaseView<Presenter> { /* 與數據相關的一些方法*/ void showAddTask(); void showTaskDetailsUi(String taskId); void showTasks(List<Task> tasks); /*告知Presenter層View層的一些狀態相關的方法。*/ boolean isActive(); /*單純的界面顯示方法*/ void showFilteringPopUpMenu(); ...省略,一系列界面的顯示與提示。具體請查看官方完整代碼。 } interface Presenter extends BasePresenter { /**與界面交互的一些方法*/ void result(int requestCode, int resultCode); void addNewTask(); /*對數據增刪改查的一系列操做*/。 void loadTasks(boolean forceUpdate); void openTaskDetails(@NonNull Task requestedTask); void completeTask(@NonNull Task completedTask); void activateTask(@NonNull Task activeTask); void clearCompletedTasks(); /**一些與數據相關的和界面相關的過程變量*/ void setFiltering(TasksFilterType requestType); TasksFilterType getFiltering(); } }
由上面的接口定義可知,
View層定義是一些與數據相關的並與界面相關的一些方法,例如
或者是一些Presenter層與View層的交互時,Presenter須要知道的View層的一些生命週期狀態相關的方法。例如
或者單純的界面的提示的一些方法如void showNoTasks()
一些與數據操做無關的方法,通常不定義在View層的接口裏,如從列表頁跳轉到統計頁就是與數據操做無關的方法,相應的由於View層不處理數據,因此View層只處理動做與數據展現,Presenter層處理數據相關的邏輯。
Data層的實例建立須要的對象與參數
private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource, @NonNull TasksDataSource tasksLocalDataSource) { mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource); mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
View層
todo‑mvp的官方示例中,View層就是一個實現了TasksContract.View接口的Fragment,和普通的Fragment並無什麼的不一樣
public static TasksFragment newInstance() { return new TasksFragment(); }
Presenter層的實例建立須要的對象與參數
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); }
從上面的構造方法中能夠知道。
Presenter用成員變量組合的方式, 持有 了View層實例與Data數據層的實例,
而 mTasksView.setPresenter(this);這句也代表了View層也持有Presenter的引用
數據層是獨立的一層,只提供一個單供給外部對象用。這樣就實現了
界面與數據邏輯操做的分離,View層只關注與界面的展現。
下面分析一下,數據查、改、增、刪操做時,MVP三層工做的過程。
用一個加載所有Task列表的過程來講明MVP對數據查找與展現的過程。
獲取所有Task的顯示和獲取Active狀態和Completed狀態的Task列表顯示是相同的過程,只是在
View層在調用 Presenter層的void loadTasks(boolean forceUpdate);前先調用了Presenter的void setFiltering(TasksFilterType requestType);,加載數據時,會判斷當前的TasksFilterType 並加載相應類型的數據。
Task狀態的改變等操做與加載列表顯示的過程大同小異。
官方todo‑mvp項目我的總結。
1.定義數據層,View層,和Presenter接口,View層接口通常定義一些與數據展現有關的方法和界面上的操做會引發數據變化須要Presenter邏輯處理數據後過才能展現的接口方法。
2.View層和Presenter層相互持有對方的實例引用,數據層只被Presenter層持有,不被View層持有,全部須要處理數據的邏輯都放在Presenter層處理。這樣就達到了View層與數據層分離。