Google官方MVP示例 todo-mvp基础架构项目分析
项目的app/src/main/java目录中代码的组织方式是按照功能模块组织的,每个功能为一个包,每个功能模块的内部分为xActivity、xFragment、xPresenter、xContract四个类文件,其作用如下:
xActivity主要负责创建View和Presenter实例,并将它们联系起来
xFragment实现View接口,作为MVP结构中的View
xPresenter实现Presenter接口,是MVP结构中的Presenter
xContract作为合同接口,统一管理View和Presenter的接口,便于查看和维护View和Presenter的功能
Model作为单独的模块存放与data目录下
项目的app/src/main/java目录中结构如下:
![]() |
---|
MVP实现方式-View和Present
以添加或编辑任务功能模块(addedittask)为例
1.View和Present基础接口
- BaseView接口中有setPresenter方法,用于将Presenter实例传入View中,在Presenter的实现类的构造方法中调用。
public interface BaseView<T> {
void setPresenter(T presenter);
}
- BasePresenter接口中有start方法,用于Presenter开始获取数据并调用View进行界面显示,在View的实现类Fragment中的onResume方法中调用。
public interface BasePresenter {
void start();
}
2.AddEditTaskContract
AddEditTaskContract是一个合同接口,其中包含了View和Presenter接口,便于查看和维护View和Presenter的功能。
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();
}
}
3.AddEditTaskActivity
AddEditTaskActivity中创建了View的实现类AddEditTaskFragment的实例和Presenter的实现类AddEditTaskPresenter的实例,并把它们联系起来。
public class AddEditTaskActivity extends AppCompatActivity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.addtask_act);
...
// 创建AddEditTaskFragment实例
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);
}
// 创建Presenter实例,传入Model实例和AddEditTaskFragment实例,建立View和Presenter之间的联系
new AddEditTaskPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
addEditTaskFragment);
}
...
}
4.AddEditTaskFragment
AddEditTaskFragment实现了AddEditTaskContract接口中的View接口,其中有一个Presenter实例,在onResume方法中调用Presenter的start方法。
public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View {
private AddEditTaskContract.Presenter mPresenter;
public static AddEditTaskFragment newInstance() {
return new AddEditTaskFragment();
}
public AddEditTaskFragment() {
// Required empty public constructor
}
@Override
public void onResume() {
super.onResume();
// 调用start方法
mPresenter.start();
}
// 重写setPresenter方法
@Override
public void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) {
mPresenter = checkNotNull(presenter);
}
...
}
5.AddEditTaskPresenter
AddEditTaskPresenter实现了AddEditTaskContract接口中的Presenter接口,其中有一个View实例,在构造方法中调用View的setPresenter方法与View建立联系。
public class AddEditTaskPresenter implements AddEditTaskContract.Presenter,
TasksDataSource.GetTaskCallback {
// Model实例,完成数据获取
@NonNull
private final TasksDataSource mTasksRepository;
@NonNull
private final AddEditTaskContract.View mAddTaskView;
@Nullable
private String mTaskId;
public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository,
@NonNull AddEditTaskContract.View addTaskView) {
mTaskId = taskId;
mTasksRepository = checkNotNull(tasksRepository);
mAddTaskView = checkNotNull(addTaskView);
mAddTaskView.setPresenter(this);
}
// 重写start方法,开始获取数据
@Override
public void start() {
if (mTaskId != null) {
populateTask();
}
}
...
}
MVP实现方式-Model
![]() |
---|
TasksDataSource定义了Model的回调接口和方法
public interface TasksDataSource {
interface LoadTasksCallback {
void onTasksLoaded(List<Task> 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);
}
1.TasksLocalDataSource
TasksLocalDataSource是TasksDataSource的本地数据源实现类,用于从本地数据库获取数据,采用单例模式,并且有两个帮助类TasksDbHelper和TasksPersistenceContract用于从操作数据库
2.TasksRemoteDataSource
TasksRemoteDataSource是TasksDataSource的远程数据源实现类,用于从服务端获取数据(demo里只是模仿从服务端获取),采用单例模式
3.TasksRepository
Repository也是TasksDataSource的实现类,其中持有两个两个TasksDataSource对象,一般为一个TasksLocalDataSource对象和一个TasksRemoteDataSource对象,用于统一管理获取数据的方式,采用单例模式
public class TasksRepository implements TasksDataSource {
private static TasksRepository INSTANCE = null;
// 远程数据源对象
private final TasksDataSource mTasksRemoteDataSource;
// 本地数据源对象
private final TasksDataSource mTasksLocalDataSource;
// 存放缓存数据
Map<String, Task> mCachedTasks;
// 缓存数据是否可用
boolean mCacheIsDirty = false;
// 构造方法
private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
@NonNull TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
}
// 获取TasksRepository实例,采用单例模式
public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource,
TasksDataSource tasksLocalDataSource) {
if (INSTANCE == null) {
INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource);
}
return INSTANCE;
}
public static void destroyInstance() {
INSTANCE = null;
}
// 获取数据
@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 {
// 从数据库获取数据,如果没有,则从服务端获取
mTasksLocalDataSource.getTasks(new LoadTasksCallback() {
@Override
public void onTasksLoaded(List<Task> 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<Task> tasks) {
refreshCache(tasks);
refreshLocalDataSource(tasks);
callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
}
@Override
public void onDataNotAvailable() {
callback.onDataNotAvailable();
}
});
}
}
可以看到,在实例化TasksRepository时需要传入两个TasksDataSource对象;在获取数据时先从缓存中获取,如果缓存中没有,则从数据库获取,数据库没有,再从服务端获取,若为强制刷新,则直接从服务端获取。
Model使用
1.TasksDataSource的实例化
在MVP中,Presenter对象持有Model对象,如:
public class AddEditTaskPresenter implements AddEditTaskContract.Presenter,
TasksDataSource.GetTaskCallback {
// TasksDataSource对象
@NonNull
private final TasksDataSource mTasksRepository;
// 实例化时传入TasksDataSource对象
public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository,
@NonNull AddEditTaskContract.View addTaskView) {
mTaskId = taskId;
mTasksRepository = checkNotNull(tasksRepository);
mAddTaskView = checkNotNull(addTaskView);
mAddTaskView.setPresenter(this);
}
}
Presenter在实例化是需要传入TasksDataSource对象,Presenter是在Activity中完成实例化的
public class AddEditTaskActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.addtask_act);
...
// 实例化Presenter,传入TasksDataSource对象
new AddEditTaskPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
addEditTaskFragment);
}
}
可以看到TasksDataSource对象是由Injection类的provideTasksRepository方法生成
2.Gradle动态配置
在main/java目中并没有Injection类,通过查看app/build.gradle文件可以知道,项目有两个生产版本:mock和prod,如下:
productFlavors {
mock {
applicationIdSuffix = ".mock"
}
prod {
}
}
在项目的目录结构中也有对应的src/mock和src/prod两个目录目录
![]() |
---|
在mock和prod中分别有一个Injection类,这里利用了gradle中的productFlavor创建了多个版本,其中每个版本都可以在工程src目录下建立与main同级的文件夹,文件夹名字就是ProductFlavor的名字,可以在其中创建java目录并添加相应的java文件,不用的版本可以有相同名字不同实现的类,具体可查看Android Plugin for Gradle
prod目录下的Injection
public class Injection {
public static TasksRepository provideTasksRepository(@NonNull Context context) {
checkNotNull(context);
return TasksRepository.getInstance(TasksRemoteDataSource.getInstance(),
TasksLocalDataSource.getInstance(context));
}
}
mock目录下的Injection
public class Injection {
public static TasksRepository provideTasksRepository(@NonNull Context context) {
checkNotNull(context);
return TasksRepository.getInstance(FakeTasksRemoteDataSource.getInstance(),
TasksLocalDataSource.getInstance(context));
}
}
可以看到:
- prod的Injection创建TasksRepository实例时传入的是TasksRemoteDataSource实例和TasksLocalDataSource实例
- mock的Injection创建TasksRepository实例时传入的是FakeTasksRemoteDataSource实例和TasksLocalDataSource实例,其中FakeTasksRemoteDataSource是在mock目录下创建的TasksDataSource的实现类。
通过这种方式可以实现不同版本注入不同的TasksDataSource实现类。