前言
android-architecture是google官方推出的关于Android架构设计的开源项目,相当于为开发者提供了在Android APP架构设计方面的指南,所以学习价值不言而喻。
该项目展示了多种不同的架构概念和工具来实现相同的应用程序,这里我们将挑选其中一些经典的架构方式对其Demo进行源码分析。
这一篇,我们对标准MVP架构的todo‑mvp进行分析。
项目结构
android-architecture一系列Demo都实现了一个TODO-LIST项目,分为任务列表(tasks)、任务详情(taskdetail)、添加任务(addedittask)、任务统计(statistics)四个功能模块。
从下图分包情况也可以看出,todo‑mvp项目是按功能模块划分包名,清晰易懂。
- addedittask:添加任务
- data:主要对数据来源实现了封装
- statistics:任务统计
- taskdetail:任务详情
- tasks:任务列表
- util:帮助类
- BasePresenter:Presenter基类
- BaseView:View基类
架构思路
标准MVP架构有别于Android传统MVC架构最明显的地方在于,View与Model不发生联系,通过Presenter传递。而View不再承担逻辑功能,只负责显示视图页面,逻辑由Presenter进行处理。
在todo‑mvp项目中,每一个具体模块都由Model、Presenter、View三部分来实现。
- Model:data包内的数据来源的封装实现类,分为本地来源和远程来源。
- Presenter:由Activity创建Presenter实例对象。
- View:由Activity创建,并由Fragment作为View的具体实现。
上图为官方给出的结构图,清晰的展示了MVP各部分的具体实现。
代码分析
在MVP模式中,每一个功能模块都由Model、Presenter、View组成,我们用addedittask模块来展示代码。
AddEditTaskContract类:
/**
* This specifies the contract between the view and the presenter.
*/
public interface AddEditTaskContract {
interface View extends BaseView {
void showEmptyTaskError();
void showTasksList();
void setTitle(String title);
void setDescription(String description);
boolean isActive();
}
interface Presenter extends BasePresenter {
void saveTask(String title, String description);
void populateTask();
boolean isDataMissing();
}
}
Contract类作为View和presenter之间的协议,封装了View和Presenter接口。MVP模式下,分出View接口和Persenter接口主要是为了将View和Persenter通过接口的方式解耦,而Contract将两者封装到一起,又可以方便查找和修改。
View接口中封装了对页面展示绘制渲染等相关操作,Persenter接口中封装了对程序逻辑、对接Model等相关操作。View接口和Presenter接口又分别继承了BaseView和BasePresenter。
public interface BaseView {
void setPresenter(T presenter);
}
BaseView中需要保持对Presenter的引用。
public interface BasePresenter {
void start();
}
BasePresenter中的start方法主要用于一些初始化操作。
AddEditTaskActivity类中创建AddEditTaskPresenter实例:
// Create the presenter
mAddEditTaskPresenter = new AddEditTaskPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
addEditTaskFragment,
shouldLoadDataFromRepo);
Activity在MVC模式下,需要承担View和Controller两者的职责,代码相当繁重。而在MVP模式下,不再承担过多的功能。创建Presenter时传入Model和View的实例,具体逻辑操作都由Presenter来负责。
Presenter
public class AddEditTaskPresenter implements AddEditTaskContract.Presenter,
TasksDataSource.GetTaskCallback {
@NonNull
private final TasksDataSource mTasksRepository;
@NonNull
private final AddEditTaskContract.View mAddTaskView;
@Nullable
private String mTaskId;
private boolean mIsDataMissing;
/**
* Creates a presenter for the add/edit view.
*
* @param taskId ID of the task to edit or null for a new task
* @param tasksRepository a repository of data for tasks
* @param addTaskView the add/edit view
* @param shouldLoadDataFromRepo whether data needs to be loaded or not (for config changes)
*/
public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository,
@NonNull AddEditTaskContract.View addTaskView, boolean shouldLoadDataFromRepo) {
mTaskId = taskId;
mTasksRepository = checkNotNull(tasksRepository);
mAddTaskView = checkNotNull(addTaskView);
mIsDataMissing = shouldLoadDataFromRepo;
mAddTaskView.setPresenter(this); //将Presenter实例对象赋给View
}
@Override
public void start() {
if (!isNewTask() && mIsDataMissing) {
populateTask();
}
}
...
}
AddEditTaskPresenter实现了AddEditTaskContract.Presenter接口,并在构造函数中获取了Model和View的实例,具体业务逻辑就是对这两个实例对象进行操作来实现。
mTasksRepository:相当于Model的实例对象,对数据操作的具体实现。
mAddTaskView:相当于View的实例对象,页面展示的具体实现。
start()方法中执行了对Model对象(mTasksRepository)赋值和初始化工作。
View
public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View {
private AddEditTaskContract.Presenter mPresenter;
@Override
public void onResume() {
super.onResume();
mPresenter.start();
}
@Override
public void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) {
mPresenter = checkNotNull(presenter);
}
...
}
AddEditTaskFragment实现了AddEditTaskContract.View接口,通过setPresenter方法获得Presenter实例,在onResume方法中执行Presenter的初始化操作,而当View接收到触发事件时,由Presenter实例来负责响应逻辑。
Model
public class TasksRepository implements TasksDataSource {
private static TasksRepository INSTANCE = null;
private final TasksDataSource mTasksRemoteDataSource;
private final TasksDataSource mTasksLocalDataSource;
// Prevent direct instantiation.
private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
@NonNull TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
}
...
}
TasksRepository作为Model层的实现类,实现了TasksDataSource接口,封装了本地数据和远程数据两种数据来源,通过TasksRepository实例对象可对数据接口做统一操作。
总结
MVP架构较之以前的MVC架构,在代码模块解耦方面表现更出色,并且解决了Activity责任繁杂的问题。
- View:只负责显示、绘制、渲染等工作。
- Model:负责数据操作,如数据库、网络数据、缓存等。
- Presenter:作为View和Model的桥梁,并负责程序逻辑。
这篇博客是Android架构一系列博客的第一篇,而且是比较标准的MVP架构,看起来有些老调重弹。后续将逐步分析android-architecture中一些比较成熟的架构,并分析与标准MVP架构的优劣。