解读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 {
// 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 {
void showTasks(List 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 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 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 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 tasks) {
List tasksToShow = new ArrayList();
// 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
如果我的文章对您有帮助,不妨点个赞鼓励一下(^_^)