一、概述
每一個 app 的運營都需要經過不斷的迭代與更新!在產品不斷的升級過程中,項目的代碼量會變得越來越大。當采用 Android 原生的架構( Android 項目的架構:當建立起一個新項目時,默認的就像是個 MVC 的架構)去不斷完善、升級項目時。到最后項目就變得越來越臃腫,這時,隨著項目的越來越大,也許不得已需要進行項目的重構,然而這是個工作量很大的任務。所以,做好項目的架構,寫好模型往往是很重要的。
google 官方在 GitHub 上也有著對應的官方規范已完善的開源項目架構(可以直接到這里下載 demo 學習):
- todo-mvp/- Basic Model-View-Presenter architecture(基本的 mvp 架構)
- todo-mvp-loaders/- Based on todo-mvp, fetches data using Loaders.(基于 todo-mvp ,數據獲取采用 Loaders)
- todo-databinding/- Based on todo-mvp, uses the Data Binding Library.(基于 todo-mvp ,使用 databinding 開源庫)
- todo-mvp-clean/- Based on todo-mvp, uses concepts from Clean Architecture.(基于 todo-mvp,使用 Clean 架構)
- todo-mvp-dagger/- Based on todo-mvp, uses Dagger2 for Dependency Injection(基于 todo-mvp,使用 dagger2 進行依賴注入)
- todo-mvp-contentproviders/- Based on todo-mvp-loaders, fetches data using Loaders and uses Content Providers(基于 todo-mvp-loaders ,數據獲取采用 Loaders 和 ContentProviders)
- todo-mvp-rxjava/- Based on todo-mvp, uses RxJava for concurrency and data layer abstraction.(基于 todo-mvp ,使用了 Rxjava )
二、Android Architecture Blueprints:todo-mvp 學習
(1)首先,我們來看一下 todo-mvp 的包結構:
我們可以看到,在 todo-mvp demo 中,一個包就對應著一個功能模塊。在 tasks 包中:
- TasksContract(定義 Presenter、View 的接口)
- TasksFragment(實現了 TasksContract.View 接口定義的功能)
- TasksPresenter(實現了 TasksContract.Presenter 接口定義的功能) 4. TasksActivity(TasksFragment 的載體)
在 data 包中,則是定義了對應的功能需要的數據實體、model 接口:
- 任務實體 Task
- model 接口 TasksDataSource
- 對應功能的 TasksDataSource實現類
(2)源碼分析
- 首先,我們先看一下Presenter、View的兩個基類
public interface BasePresenter { void start();}
start()方法在View界面開始顯示的時候調用,一般是在Activity、Fragment的onStart()方法中
public interface BaseView<T> { void setPresenter(T presenter);}
setPresenter(T presenter)方法在Presenter實例化之后調用,一般是在Presenter構造方法最后調用,傳入自己。
- 我們從 TasksActivity 入手,在 onCreate() 方法中,可以看到下面一段代碼
@Override
protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState);
...略
//添加TasksFragment
TasksFragment tasksFragment = (TasksFragment)getSupportFragmentManager().findFragmentById(R.id.contentFrame);
if (tasksFragment == null) {
tasksFragment = TasksFragment.newInstance();
ActivityUtils.addFragmentToActivity( getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
}
//新建TasksPresenter實例
mTasksPresenter = new TasksPresenter(Injection.provideTasksRepository(getApplicationContext()), tasksFragment);
}
TasksActivity 中,在添加 TasksFragment 后 new 了一個 TasksPresenter,接下來看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);
}
TasksPresenter的構造方法中,TasksPresenter獲得了對mTasksView引用,并且還調用了mTasksView的setPresenter()方法。此時,TasksPresenter就傳遞到了TasksFragment 中,只要在TasksFragment 中對mPresenter 進行賦值,就完成了TasksFragment 與TasksPresenter實例引用的相互持有。
@Override public void setPresenter(@NonNull TasksContract.Presenter presenter) {
mPresenter = presenter;
}
到這里你也許會問,你只說了 V 與 P 的聯系,那 M 去哪里了呢?嘿嘿,你在返回去看TasksPresenter的構造方法,這時你是否會發現這一句代碼。
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");
沒錯,就是在實例化TasksPresenter的同時,將數據處理model TasksRepository 賦予了TasksPresenter。至此,既然知道了各自的引用關系,那View、Presenter、Model的關系也就清楚了。
注意:Presenter的實例化在Activity中,但View對Presenter的引用卻不一定在Activity中。因為View的實現類不一定是Activity,也可能是Fragment。至此,我們可以得出一個結論:Presenter的實例化在Activity中,View對Presenter的引用在View的實現類中。
3.既然清楚了View、Presenter、Model之間的關系,那么我們來看看它們之間到底是怎樣協作的。由于代碼比較多,我們就選擇刷新數據功能來講。
- View
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...略
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mPresenter.loadTasks(false);
}
});
...略
return root;
}
在刷新時,調用了 mPresenter.loadTasks(false);可以看到,View將刷新功能交給了Presenter來做。再來看看mPresenter.loadTasks(false)方法 - Presenter
@Override
public void loadTasks(boolean forceUpdate) {
loadTasks(forceUpdate || mFirstLoad, true);
mFirstLoad = false;
}
//私有方法,操作數據
private void loadTasks(boolean forceUpdate, final boolean showLoadingUI) {
if (showLoadingUI) {
//對mTasksView的選中框進行顯示
mTasksView.setLoadingIndicator(true);
}
if (forceUpdate) {
//mTasksRepository中的數據操作、刷新數據
mTasksRepository.refreshTasks();
}
EspressoIdlingResource.increment();
// App is busy until further notice
//mTasksRepository中的回調方法處理返回的數據
mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() {
@Override
public void onTasksLoaded(List<Task> tasks) {
List<Task> tasksToShow = new ArrayList<Task>();
// This callback may be called twice, once for the cache and once for loading
// the data from the server API, so we check before decrementing, otherwise
// it throws "Counter has been corrupted!" exception.
if (!EspressoIdlingResource.getIdlingResource().isIdleNow()) {
EspressoIdlingResource.decrement();
// Set app as idle.
}
// We filter the tasks based on the requestType
for (Task task : tasks) {
switch (mCurrentFiltering) {
case ALL_TASKS:
tasksToShow.add(task);
break;
case ACTIVE_TASKS:
if (task.isActive()) {
tasksToShow.add(task);
}
break;
case COMPLETED_TASKS:
if (task.isCompleted()) {
tasksToShow.add(task);
}
break;
default:
tasksToShow.add(task);
break;
}
}
// The view may not be able to handle UI updates anymore
if (!mTasksView.isActive()) {
return;
}
if (showLoadingUI) {
mTasksView.setLoadingIndicator(false);
}
processTasks(tasksToShow);
}
@Override
public void onDataNotAvailable() {
// The view may not be able to handle UI updates anymore
if (!mTasksView.isActive()) {
return;
}
mTasksView.showLoadingTasksError();
}
});
}
mPresenter.loadTasks()方法通過調用一個同名私有的重載的方法,調用 mTasksView.setLoadingIndicator(true)顯示是選中框、 mTasksRepository.refreshTasks()刷新數據、 mTasksRepository.getTasks()的回調方法處理返回的數據。
總結:MVP在我看來就是一種“代理模式”。View、Model只需要做好自己的任務,然后,Model處理后的數據交于Presenter,Presenter通過View的引用將處理后的數據進行顯示。最后,還有個契約類,也就是Contract,主要用于管理Presenter與View的接口,方便擴展。