已經使用了一段時間的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的實現更加靈活。