博客主頁
解讀Google官方MVP
- todo-mvp:mvp基礎架構
- deprecated-todo-mvp-loaders:基於todo-mvp,獲取數據使用Loaders,已過時
- deprecated-todo-mvp-contentproviders:基於todo-mvp-loaders, 使用Content Providers,已過時
- deprecated-todo-databinding:基於todo-mvp,使用數據綁定組件,已過時
- todo-mvp-clean:基於todo-mvp, 採用Clean架構的概念
- todo-mvp-dagger:基於todo-mvp,使用Dagger2進行依賴注入
- todo-mvp-rxjava:基於todo-mvp,使用rxjava
- todo-mvp-kotlin:基於todo-mvp,使用kotlin
官方網址:https://github.com/android/ar...
1. 基於todo-mvp分析
該示例有四個界面功能:任務列表、任務詳情、任務添加編輯、任務統計
代碼結構:按照功能分包,Activity、Fragment、Contract、Presenter四種類文件
測試代碼結構:androidTest(UI層測試)、androidTestMock(UI層測試mock數據支持)、test(業務層單元測試)、mock(業務層單元測試mock數據支持)
2. 源碼分析
1、首先看下兩個Base接口的基類:BaseView與BasePresenter,分別是所有View與Presenter的基類
// 接受一個泛型參數,泛型類為Presenter的具體實現類
public interface BaseView<T> {
// View必須實現setPresenter方法,View持有對Presenter的引用
void setPresenter(T presenter);
}
setPresenter的調用時機在Presenter具體實現的構造方法中,這樣View與Presenter關聯起來了。
public interface BasePresenter {
// 規定Presenter必須要實現start方法
void start();
}
該start方法的作用:Presenter開始獲取數據並調用View的方法刷新UI,調用時機是在Activity(或者Fragment)的onResume方法。
2、定義契約類(合同)
Google引用契約類,主要作用是用來統一管理View和Presenter接口,使得View和Presenter中有哪些功能,一目瞭然,便於維護。下面通過任何列表界面來分析:
public interface TasksContract {
interface View extends BaseView<Presenter> {
void showTasks(List<Task> tasks);
// ... 省略其它的UI操作
}
interface Presenter extends BasePresenter {
void loadTasks(boolean forceUpdate);
// ... 省略其它的業務操作
}
}
- TasksContract(代表契約、合同)中的View接口定義了該界面中所有UI操作;TasksFragment作為View層,實現了該接口(也就是View接口),TasksFragment只需要關心UI相關的操作,所有的事件操作都通過TasksPresenter完成
- Presenter接口定義了該界面中所有操作事件;TasksPresenter作為Presenter層,實現了該接口(也就是Presenter接口),TasksPresenter只關心業務相關的邏輯,UI狀態更新通過View層的方法
3、Model層
它的作用主要用來獲取數據、存取數據、數據狀態更新等。先看下TasksRepository的getTasks方法
/**
* Gets tasks from cache, local data source (SQLite) or remote data source, whichever is available first.
*/
@Override
public void getTasks(@NonNull final LoadTasksCallback callback) {
checkNotNull(callback); // 判空處理,提前發現異常的方式
// 如果內存中有緩存數據並且不是髒數據,立即返回
if (mCachedTasks != null && !mCacheIsDirty) {
callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
return;
}
if (mCacheIsDirty) {
// 如果緩存中是髒數據,需要從網絡獲取新的數據
getTasksFromRemoteDataSource(callback);
} else {
// 如果本地SQLite中緩存數據可用,返回;否則從網絡獲取新的數據
mTasksLocalDataSource.getTasks(new LoadTasksCallback() {
@Override
public void onTasksLoaded(List<Task> tasks) {
refreshCache(tasks); // 更新內存中緩存數據
callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values())); // 成功回調
}
@Override
public void onDataNotAvailable() {
// 回調該方法,説明緩存中的數據不可用,從網絡獲取新的數據
getTasksFromRemoteDataSource(callback);
}
});
}
}
private void getTasksFromRemoteDataSource(@NonNull final LoadTasksCallback callback) {
mTasksRemoteDataSource.getTasks(new LoadTasksCallback() {
@Override
public void onTasksLoaded(List<Task> tasks) {
refreshCache(tasks); // 更新內存中緩存數據
refreshLocalDataSource(tasks); // 更新SQLite中數據
callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values())); // 成功回調數據
}
@Override
public void onDataNotAvailable() {
callback.onDataNotAvailable(); // 沒有可用的數據
}
});
}
TasksRepository中維護着兩個數據源:mTasksRemoteDataSource(從網絡中獲取的數據源)和 mTasksLocalDataSource(從本地SQLite獲取的數據源)
// 遠程端數據源
private final TasksDataSource mTasksRemoteDataSource;
// 本地SQLite數據源
private final TasksDataSource mTasksLocalDataSource;
這兩個數據源都實現了TasksDataSource接口,其中TasksRepository也實現了該接口
public interface TasksDataSource {
interface LoadTasksCallback {
void onTasksLoaded(List<Task> tasks);
void onDataNotAvailable();
}
interface GetTaskCallback {
void onTaskLoaded(Task task);
void onDataNotAvailable();
}
void getTasks(@NonNull LoadTasksCallback callback);
void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);
void saveTask(@NonNull Task task);
void completeTask(@NonNull Task task);
void completeTask(@NonNull String taskId);
void activateTask(@NonNull Task task);
void activateTask(@NonNull String taskId);
void clearCompletedTasks();
void refreshTasks();
void deleteAllTasks();
void deleteTask(@NonNull String taskId);
}
4、Presenter層
TasksPresenter實現TasksContract.Presenter接口,重寫BasePresenter的start()方法。當收到View層的數據請求後,Presenter層控制Model層進行業務邏輯處理,Model層處理完畢後,把數據返回給Presnerer層,然後通知View層進行UI更新。
// TasksPresenter.java
@Override
public void start() {
loadTasks(false);
}
private void loadTasks(boolean forceUpdate, final boolean showLoadingUI) {
if (showLoadingUI) {
mTasksView.setLoadingIndicator(true);
}
if (forceUpdate) {
mTasksRepository.refreshTasks();
}
mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() {
@Override
public void onTasksLoaded(List<Task> tasks) {
List<Task> tasksToShow = new ArrayList<Task>();
// 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();
}
});
}
5、View層
負責View視圖和Presenter的創建,並將二者關聯起來;View層持有Presenter後,通過它進行一系列的操作
// TasksActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tasks_act);
// ...
// 創建Presenter對象,將當前的View傳給Presenter層
mTasksPresenter = new TasksPresenter(
Injection.provideTasksRepository(getApplicationContext()), tasksFragment);
}
// TasksPresenter.java
// TasksPresenter的構造方法
public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) {
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");
mTasksView = checkNotNull(tasksView, "tasksView cannot be null!");
// View與Presenter建立關聯
mTasksView.setPresenter(this);
}
總結
Fragment作為View層,View和Presenter通過Activity建立關聯,Presenter對數據的調用通過TasksRepository完成,而TasksRepository維護着自己的數據源和實現。
依賴
1、本地數據庫SQLite使用Room框架:
https://developer.android.goo...
2、Android中guava的核心類庫com.google.guava:guava:28.1-android
https://github.com/google/guava
3、mockito:A mocking framework used to implement unit tests.
https://site.mockito.org/
4、Android Testing Support Library: A framework used to support UI tests, using both Espresso, and AndroidJUnitRunner.
https://developer.android.goo...
5、Common Android support libraries:Packages in the com.android.support.* namespace provide backwards compatibility and other features.
https://developer.android.goo...
開源MVP框架
1. TheMVP
https://github.com/kymjs/TheMVP
用MVP架構開發Android應用:https://kymjs.com/code/2015/1...
TheMVP使用Activity作為Presenter層來處理代碼邏輯,通過讓Activity包含一個ViewDelegate對象來間接操作View層對外提供方法,從而做到完全解耦視圖層。
2. MVPro
https://github.com/qibin0506/...
都是將Activity和Fragment作為Presenter。Presenter即我們的Activity或者Fragment,View呢?説白了就是我們從Activity和Fragment中提取出來的和View操作相關的代碼
3. nucleus
https://github.com/konmik/nuc...
4. Beam
https://github.com/Jude95/Beam
如果我的文章對您有幫助,不妨點個贊鼓勵一下(^_^)