今天週六抽時間把google的2個 MVP Demo看一下,寫篇文章記錄一下.
先來看一下第一個最基礎的todo-mvp
下圖是Google MNP第一個Demo的架構圖,至於為什麼要使用Activity+Fragment,這裡是把Activity當做了控制器(Controllers),負責建立Frament和Presenter並連線他們。Activity和Repository之間的圓圈代表他們直接主要通過介面互動。
下面看原始碼
BasePresenter中需要定義一些所有Presenter的基礎行為,Demo中定義一個start方法
BaseView定義了設定Presenter的方法,每個View都需要有一個對應的Presenter.
每個Fragment都會有一個契約類,用來定義具體的BaseView和Presenter介面,從這個契約類能後一眼看出Presenter和UI有什麼操作,比如TaskDetailContract中
interface View extends BaseView<Presenter> {
void setLoadingIndicator(boolean active);
void showMissingTask();
void hideTitle();
void showTitle(String title);
void hideDescription();
void showDescription(String description);
void showCompletionStatus(boolean complete);
void showEditTask(String taskId);
void showTaskDeleted();
void showTaskMarkedComplete();
void showTaskMarkedActive();
boolean isActive();
}
interface Presenter extends BasePresenter {
void editTask();//編輯task
void deleteTask();
void completeTask();
void activateTask();
}複製程式碼
下面進入TaskDetailActivity 看一下
以下是OnCreate的程式碼片段,UI的就省略了,建立了Fragment並新增到Activity
ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
taskDetailFragment, R.id.contentFrame);
}
// Create the presenter
new TaskDetailPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
taskDetailFragment);複製程式碼
並且new了一個Presenter,把fragment和repository傳入presenter中,來看一下在TaskDetailPresenter裡做了什麼
public TaskDetailPresenter(@Nullable String taskId,
@NonNull TasksRepository tasksRepository,
@NonNull TaskDetailContract.View taskDetailView) {
mTaskId = taskId;
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!");
mTaskDetailView.setPresenter(this);
}複製程式碼
mTaskDetailView.setPresenter(this);很關鍵,將TaskDetailPresenter的引用傳給了fragment,並儲存了對應的view(taskDetailView),這樣V 和 P就相互儲存了對方的例項。
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
複製程式碼
可以看到TaskDetailPresenter還儲存了Model層的一個引用mTasksRepository,如果需要去獲取一個Task就是利用mTasksRepository.getTask(int id);
mTasksRepository從哪來的??其實在我們new presenter的時候構造並傳入的
// Create the presenter
new TaskDetailPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
taskDetailFragment);複製程式碼
public static TasksRepository provideTasksRepository(@NonNull Context context) {
checkNotNull(context);
ToDoDatabase database = ToDoDatabase.getInstance(context);
return TasksRepository.getInstance(FakeTasksRemoteDataSource.getInstance(),
TasksLocalDataSource.getInstance(new AppExecutors(),
database.taskDao()));
}複製程式碼
在構造TaskRepository的時候傳入了FakeTasksRemoteDataSource和TasksLocalDataSource的單例,,這2個都是提供資料的,都實現了getTask(int id)方法,分別從遠端和本地獲取,在TasksRepository中會根據情況來進行呼叫,如果本地資料來源獲取不到就從遠端資料來源獲取
@Override
public void getTask(@NonNull final String taskId, @NonNull final GetTaskCallback callback) {
//從本地資料庫獲取
mTasksLocalDataSource.getTask(taskId, new GetTaskCallback() {
@Override
public void onTaskLoaded(Task task) {
// Do in memory cache update to keep the app UI up to date
mCachedTasks.put(task.getId(), task);
callback.onTaskLoaded(task);
}
@Override
public void onDataNotAvailable() {
//從遠端獲取
mTasksRemoteDataSource.getTask(taskId, new GetTaskCallback() {
@Override
public void onTaskLoaded(Task task) {
mCachedTasks.put(task.getId(), task);
callback.onTaskLoaded(task);
}
@Override
public void onDataNotAvailable() {
callback.onDataNotAvailable();
}
});
}
});
}複製程式碼
獲取到資料之後便通過callback回到給presenter,然後在回撥裡呼叫View介面進行UI的顯示。
mTasksRepository.getTask(mTaskId, new TasksDataSource.GetTaskCallback() {
@Override
public void onTaskLoaded(Task task) {
showTask(task);
}複製程式碼
整個流程基本是這樣,來看一下時序圖
總結一下,首先Activity負責管理管理View和Presenter以及他們的連線,以及建立Model,讓Model和Presenter互相持有對方的引用,TaskRepository則負責資料來源的管理以及怎麼獲取,是否從快取中獲取還是本地資料庫或者遠端伺服器,相對於傳統的開發模式,Presenter極大的減輕了Activity的負擔,讓Activity只關注UI方面的顯示。
程式碼雖然簡單,但是真正用在專案中我感覺不會這麼簡單,第一點疑問就是這麼一個demo工程就需要建立這麼多class,這樣的話在大專案中那會有多少class!
Google提供的Demo只是一個樣本,實際使用中肯定要結合專案進行靈活架構調整