今天周六抽时间把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只是一个样本,实际使用中肯定要结合项目进行灵活架构调整