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