已經使用了一段時間的MVP模式,今天再以google官方的安卓架構示例todo-mvp爲例,再次學習MVP模式。數據庫
在學習todo-mvp以前,先了解一下MVP的優點。bash
從上圖能夠看出,todo-mvp是按照功能模塊劃分的。網絡
其中tasks, taskdetail, addedittask, statistics是四個業務模塊。架構
data是數據模塊,其中具體的類TasksRepository
擔任Model層,負責遠程和本地數據的獲取。app
BasePresenter
和BaseView
是presenter 和 view 的基類,在具體模塊承擔實際功能。最後,util是工具類集合。異步
在todoapp中,MVP的具體結構以下圖所示:ide
下面以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的實現更加靈活。