前言
MVP (Model View Presenter)架构是从MVC(Model View Controller)架构演变而来的。严格来说于Android应用开发本身就可以认为是一种MVC架构。一般来说我们会将XML文件视为MVC中的View角色,而将Activity则视为MVC中的Controller角色。不过大多数情况下Activity不仅仅是Controller,而是Controller和View的合体。我们一般会将业务逻辑代码和数据绑定代码都写在Activity中。这样在Activity中代码量就无法得到控制,少则成百上千,多的话也许无法估量。所以才慢慢有了MVP,将业务与数据分离开来。
MVP说明
MVP 简而言之就是在MVC的基础上将业务与数据剥离开来,以减少Activity臃肿的情况,同时增加了代码的可读性。
- Model:对于Model层也是数据层,用来存储数据,并用于绑定View层,但实际上Model层与View层毫无关联
- View: 对于View层也是视图层,在View层中只负责对数据的展示,提供界面与用户进行交互。通常将Activity或者Fragment作为View层或者说是xml中组成的可视化控件。
- Presenter:对于Presenter也是控制层,起到了一个中间人的作用。一般来说将网络请求放在presenter中,将获取到的数据存储到Model层,通过回调将Model中的数据传递给View层,View层与Model通过Presenter层进行交互,使得View和Model之间不存在耦合,同时也将业务逻辑从View中抽离

通过上图可以发现,View层与Model层并不存在关联。在Presenter层中包含了一个Viewer,并且依赖于Model接口,从而将Model层与View层联系在一起。大多数人应该都知道接口回调这个概念,Presenter与View之间的交互就是通过接口的。
Google官方的MVP框架
项目介绍
项目地址: github.com/googlesampl…. 可以进入到项目中看看Google官方是如何介绍MVP的。
Stable samples
| Sample | Description |
|---|---|
todo‑mvp |
Demonstrates a basic Model‑View‑Presenter (MVP) architecture and provides a foundation on which the other samples are built. This sample also acts as a reference point for comparing and contrasting the other samples in this project. |
| todo‑mvp‑clean | Uses concepts from Clean Architecture. |
| todo‑mvp‑dagger | Uses Dagger 2 to add support for dependency injection. |
| todo‑mvp‑rxjava | Uses RxJava 2 to implement concurrency, and abstract the data layer. |
| todo‑mvvm‑databinding | Based on the todo-databinding sample, this version incorporates the Model‑View‑ViewModel pattern. |
| todo‑mvvm‑live | Uses ViewModels and LiveData from Architecture Components and the Data Binding library with an MVVM architecture. |
Samples in progress
| Sample &nsp | Description |
|---|---|
| dev‑todo‑mvvm‑rxjava | Based on the todo-rxjava sample, this version incorporates the Model‑View‑ViewModel pattern. |
从上述介绍中可以看到,官方给出的所有架构都是基于MVP架构,所以我们可以看看基础架构todo-mvp。
先看看项目结构


在最外层很清晰的可以看到两个接口BasePresenter和BaseView。它们是Presenter层接口和View层接口的基类,项目中所有的Presenter接口和View层接口都继承自这两个接口。
MVP架构的实现
Model层的实现
public interface TasksDataSource {
interface LoadTasksCallback {
void onTasksLoaded(List<Task> tasks);
void onDataNotAvailable();
}
......
}
TasksDataSource实现了TasksLocalDataSource,在TasksDataSource中的方法也就是一些对数据操作。而在TasksDataSource的两个内部接口LoadTasksCallback和GetTaskCallback是Model层的回调接口。它们的真正实现是在Presenter层。对于成功获取到数据后变或通过这个回调接口将数据传递Presenter层。若是获取失败也会通过回调接口来通知Presenter层。
public class TasksLocalDataSource implements TasksDataSource {
......
@Override
public void getTasks(@NonNull final LoadTasksCallback callback) {
Runnable runnable = new Runnable() {
@Override
public void run() {
final List<Task> tasks = mTasksDao.getTasks();
mAppExecutors.mainThread().execute(new Runnable() {
@Override
public void run() {
if (tasks.isEmpty()) {
// This will be called if the table is new or just empty.
callback.onDataNotAvailable();
} else {
callback.onTasksLoaded(tasks);
}
}
});
}
};
mAppExecutors.diskIO().execute(runnable);
}
@Override
public void getTask(@NonNull final String taskId, @NonNull final GetTaskCallback callback) {
Runnable runnable = new Runnable() {
@Override
public void run() {
final Task task = mTasksDao.getTaskById(taskId);
mAppExecutors.mainThread().execute(new Runnable() {
@Override
public void run() {
if (task != null) {
callback.onTaskLoaded(task);
} else {
callback.onDataNotAvailable();
}
}
});
}
};
mAppExecutors.diskIO().execute(runnable);
}
@Override
public void saveTask(@NonNull final Task task) {
checkNotNull(task);
Runnable saveRunnable = new Runnable() {
@Override
public void run() {
mTasksDao.insertTask(task);
}
};
mAppExecutors.diskIO().execute(saveRunnable);
}
......
}
可以看到在TasksLocalDataSource中实现的getTask方法,在TasksDataSource内的GetTaskCallback回调接口。在Presenter层中实现回调,将数据传递到Presenter层。
Presenter与View层提供的接口
先来看一下Presenter与View交互息息相关的两个基类接口
BasePresenter
package com.example.android.architecture.blueprints.todoapp;
public interface BasePresenter {
void start();
}
主要的作用是从Model层中获取数据。
BaseView
package com.example.android.architecture.blueprints.todoapp;
public interface BaseView<T> {
void setPresenter(T presenter);
}
主要作用是对View层中的presenter进行初始化。
AddEditTaskContract
public interface AddEditTaskContract {
interface View extends BaseView<Presenter> {
void showEmptyTaskError();
void showTasksList();
void setTitle(String title);
void setDescription(String description);
boolean isActive();
}
interface Presenter extends BasePresenter {
void createTask(String title, String description);
void updateTask( String title, String description);
void populateTask();
}
}
从代码中就可以看到是对数据的一些增删改查,以及Task的保存更新。
Presenter层
public class TaskDetailPresenter implements TaskDetailContract.Presenter {
private final TasksRepository mTasksRepository;
private final TaskDetailContract.View mTaskDetailView;
@Nullable
private String mTaskId;
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);
}
@Override
public void start() {
openTask();
}
private void openTask() {
if (Strings.isNullOrEmpty(mTaskId)) {
mTaskDetailView.showMissingTask();
return;
}
mTaskDetailView.setLoadingIndicator(true);
mTasksRepository.getTask(mTaskId, new TasksDataSource.GetTaskCallback() {
@Override
public void onTaskLoaded(Task task) {
// The view may not be able to handle UI updates anymore
if (!mTaskDetailView.isActive()) {
return;
}
mTaskDetailView.setLoadingIndicator(false);
if (null == task) {
mTaskDetailView.showMissingTask();
} else {
showTask(task);
}
}
@Override
public void onDataNotAvailable() {
// The view may not be able to handle UI updates anymore
if (!mTaskDetailView.isActive()) {
return;
}
mTaskDetailView.showMissingTask();
}
});
}
@Override
public void editTask() {
if (Strings.isNullOrEmpty(mTaskId)) {
mTaskDetailView.showMissingTask();
return;
}
mTaskDetailView.showEditTask(mTaskId);
}
......
}
从上述代码可以看到数据的获取,代码的回调,都可以放到这一层中,在这一层中可以支撑业务逻辑的实现。
View层
public class TaskDetailActivity extends AppCompatActivity {
public static final String EXTRA_TASK_ID = "TASK_ID";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.taskdetail_act);
// Set up the toolbar.
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar ab = getSupportActionBar();
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowHomeEnabled(true);
// Get the requested task id
String taskId = getIntent().getStringExtra(EXTRA_TASK_ID);
TaskDetailFragment taskDetailFragment = (TaskDetailFragment) getSupportFragmentManager()
.findFragmentById(R.id.contentFrame);
if (taskDetailFragment == null) {
taskDetailFragment = TaskDetailFragment.newInstance(taskId);
ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
taskDetailFragment, R.id.contentFrame);
}
// Create the presenter
new TaskDetailPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
taskDetailFragment);
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
}
View层可能是大家最为熟悉的了,就不多说了。
自己的MVP
接下来粗略的介绍一下我在项目中使用的MVP模式。 首先肯定是基础类了
public interface Viewer {
Activity getActivity();
}
Viewer写到接口主要是连接Presenter与Activity的主要,在Presenter中存入数据,在Activity中调用数据。当然这个是基类,你要编辑业务Viewer去继承它。
public abstract class BasePresenter<T extends Viewer> implements Presenter {
protected T viewer;
public BasePresenter(T viewer) {
this.setViewer(viewer);
}
public Activity getActivity() {
Activity activity = this.viewer == null ? null : this.viewer.getActivity();
return activity != null && !activity.isFinishing() ? activity : null;
}
public void _willDestroy() {
if (this.mPresenterHandler != null) {
this.mPresenterHandler.removeCallbacksAndMessages((Object)null);
}
}
}
BasePresenter主要写的就是网络请求,以及一些其他的操作,然后将数据存储到Viewer中。
public class **Activity extends BaseActivity implements **Viewer {
............
}
在Activity中实现Viewer,实现其中自己定义的各种方法,其实就是数据的获取操作,然后绑定View,实现可视化控件,给予用户良好的体验。
总结
通过MVP框架可以看出每个层级之间的分割是非常清晰的,也很大程度上降低了代码的耦合度。虽然看起来每次都要新建很多个类,有些人会觉得开发都忙不过来了,哪里还有时间去搞这个,但是中国不是有句古话“磨刀不误砍柴工”,所以还是要利用好MVP模式。 当然还有一些其他的MVP,比如跟dagger2结合的,比如跟rxjava结合的,比如跟databinding结合的,看个人的选择吧。 以上只是我个人的粗浅之见。