项目地址:https://github.com/googlesamples/android-architecture/tree/todo-mvp
最基础的MVP项目,是一个类似备忘录的项目。项目目录结构如下图:

- java目录下,基本是按照功能来划分,除了util是工具模块,data是Model模块。
- test目录下,是测试模块,由于是MVP模式,所以只需要对Presenter模块测试就行
不同功能的代码类似,所以可以简单看下添加备忘录功能实现模块,类比其他功能模块。
这是添加功能模块目录结构,如下:

- Fragment是View模块,只负责UI上的功能,持有Persener对象的引用
- Presenter负责逻辑代码,不涉及任何UI上的具体操作,持有View对象的引用
- Contract定义了Viwe模块和Presenter模块各自的实现接口
- Activity负责架起View模块和Presenter模块的桥梁
接下来,我们来看下具体代码的实现,先看AddEditTaskContract类的定义:
<pre>
<code>
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();
}
}
</code>
</pre>
该类分为两个部分,分别定义了View模块需要实现的接口和Persenter模块实现的接口。
再看下AddEditTaskActivity类的主要代码:
<pre>
<code>
AddEditTaskFragment addEditTaskFragment = (AddEditTaskFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
String taskId = null;
if (addEditTaskFragment == null)
{
addEditTaskFragment = AddEditTaskFragment.newInstance();
if (getIntent().hasExtra(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID))
{
taskId = getIntent().getStringExtra(
AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID);
actionBar.setTitle(R.string.edit_task);
Bundle bundle = new Bundle();
bundle.putString(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID, taskId);
addEditTaskFragment.setArguments(bundle);
} else
{
actionBar.setTitle(R.string.add_task);
}
ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
addEditTaskFragment, R.id.contentFrame);
}
// Create the presenter
new AddEditTaskPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
addEditTaskFragment);
</code>
</pre>
和AddEditTaskPresenter类的构造方法:
<pre>
<code>
public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository, @NonNull AddEditTaskContract.View addTaskView
{
mTaskId = taskId;
mTasksRepository = checkNotNull(tasksRepository);
mAddTaskView = checkNotNull(addTaskView);
mAddTaskView.setPresenter(this);
}
</code>
</pre>
综合两部分代码来看,可以知道
-
Actvity中实例化AddEditTaskFragment,并通过调用AddEditTaskPresenter构造方法,将Fragment传递给Presenter
-
AddEditTaskPresenter中又通过setPresenter将本身赋值给AddEditTaskFragment
这样AddEditTaskFragment和AddEditTaskPresenter各自拥有对方实例的引用
下面是新建新的备忘录代码:
<pre>
<code>
mPresenter.createTask(mTitle.getText().toString(),
mDescription.getText().toString());
</code>
</pre>
<pre>
<code>
@Override
public void createTask(String title, String description)
{
Task newTask = new Task(title, description);
if (newTask.isEmpty())
{
mAddTaskView.showEmptyTaskError();
} else
{
mTasksRepository.saveTask(newTask);
mAddTaskView.showTasksList();
}
}
</code>
</pre>
当点击新建按钮,Fragment就调用Presenter的createTask,让Persenter来实现具体的代码,而在Perseter中当逻辑代码执行完毕时,由通过调用Fragment的showEmptyTaskError或showTasksList来更新UI,通知用户操作结果。这样子,就将代码逻辑和界面更新两个操作完全分离开来。
接下来,我们来看下Test测试代码,就知道上面这样做的好处了:
<pre>
<code>
@Test
public void saveNewTaskToRepository_showsSuccessMessageUi()
{
// Get a reference to the class under test
mAddEditTaskPresenter = new AddEditTaskPresenter("1", mTasksRepository, mAddEditTaskView);
// When the presenter is asked to save a task
mAddEditTaskPresenter.createTask("New Task Title", "Some Task Description");
// Then a task is saved in the repository and the view updated
verify(mTasksRepository).saveTask(any(Task.class)); // saved to the model
verify(mAddEditTaskView).showTasksList(); // shown in the UI
}
</code>
</pre>
这里我们只看主要代码,先实例化一个Presenter对象,调用createTask来模拟新增新的备忘录,通过verify来检查saveTask和showTasksList两个方法是否被调用。
小结一下:MVP模式,对android代码来说是个很好的设计模式,将界面UI操作和逻辑代码实现完全分离开来,不管是写单元测试代码,还是后期功能改动都是比较方便的
网友评论