Hiten's Blog.

解读官方MVP架构

字数统计: 1.5k阅读时长: 6 min
2016/05/20 Share

在Android应用开发中采用MVC好还是MVP好的话题,一直都争议不断,现在Android官方也来凑热闹了,官方出了个开源项目,来描绘一幅架构蓝图,该项目主要包括mvp,mvvm,clean架构,其中不乏Dagger2,rxjava等主流框架,项目中使用不同的架构来实现同一个Todo App,项目地址:https://github.com/googlesamples/android-architecture

今天先来看看MVP在Todo项目中的使用,项目地址:https://github.com/googlesamples/android-architecture/tree/todo-mvp
clone工程,编译运行效果:

App功能分析:
TO-DO就是一个任务清单,首页是展示任务的清单列表,有增加任务,标记任务完成,统计等功能

打开代码目标结构:

分析包,目测

  • addedittask是增加和编辑功能
  • statistics是任务统计,
  • taskdetail是任务详情
  • tasks是任务列表
  • util是一些工具类
  • BasePresenter和BaseView是V和P的基类
  • data包是整个M层

分析V层和P层

我们先看BaseView.java

1
2
3
4
5
public interface BaseView<T> {

void setPresenter(T presenter);

}

BaseView是一个接口,里边只定义一个方法setPresenter,presenter是泛型T

再看BasePresenter.java

1
2
3
4
5
public interface BasePresenter {

void start();

}

BasePresenter也是一个接口,定义了start方法

我们打开tasks包下的TasksContract类,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public interface TasksContract {

interface View extends BaseView<Presenter> {

void setLoadingIndicator(boolean active);

void showTasks(List<Task> tasks);

void showAddTask();

void showTaskDetailsUi(String taskId);

void showTaskMarkedComplete();

void showTaskMarkedActive();

void showCompletedTasksCleared();

void showLoadingTasksError();

void showNoTasks();

void showActiveFilterLabel();

void showCompletedFilterLabel();

void showAllFilterLabel();

void showNoActiveTasks();

void showNoCompletedTasks();

void showSuccessfullySavedMessage();

boolean isActive();

void showFilteringPopUpMenu();
}

interface Presenter extends BasePresenter {

void result(int requestCode, int resultCode);

void loadTasks(boolean forceUpdate);

void addNewTask();

void openTaskDetails(@NonNull Task requestedTask);

void completeTask(@NonNull Task completedTask);

void activateTask(@NonNull Task activeTask);

void clearCompletedTasks();

void setFiltering(TasksFilterType requestType);

TasksFilterType getFiltering();
}
}

TasksContract本身是个接口,里边定义了两个接口View和Presenter,View继承自BaseView,里边定义的是具体操作UI的方法,Presenter继承自BasePresenter,里边主要是一些操作数据的方法

跟踪接口TasksContract.View的实现类TasksFragment

1
2
3
4
5
6
7
8
9
10
11
12
public class TasksFragment extends Fragment implements TasksContract.View {

private TasksContract.Presenter mPresenter;

@Override
public void setPresenter(@NonNull TasksContract.Presenter presenter) {
mPresenter = checkNotNull(presenter);
}

//省略代码

}

TasksFragment持有TasksContract.Presenter实例,在setPresenter方法将Presenter注入。

谁来调用TasksFragment的setPresenter方法,并没有发现,这个问题先放一下,先看看TasksContract.Presenter实现类TasksPresenter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TasksPresenter implements TasksContract.Presenter {

private final TasksRepository mTasksRepository;

private final TasksContract.View mTasksView;

//构造方法
public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) {
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");
mTasksView = checkNotNull(tasksView, "tasksView cannot be null!");

mTasksView.setPresenter(this);
}

}

TasksPresenter类持有TasksRepository和TasksContract.View实例,在构造方法注入,构造方法最后一句执行mTasksView.setPresenter(this);
这样就明白了刚才的问题,TasksFragment的setPresenter方法是在TasksRepository构造中注入,OK,那么TasksPresenter对象由谁创建呢?ctrl+g定位到TasksActivity,在onCreate方法中构造了TastsPresenter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class TasksActivity extends AppCompatActivity {
private TasksPresenter mTasksPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
TasksFragment tasksFragment =(TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
if (tasksFragment == null) {
// Create the fragment
tasksFragment = TasksFragment.newInstance();
ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
}
mTasksPresenter = new TasksPresenter(Injection.provideTasksRepository(getApplicationContext()), tasksFragment);


if (savedInstanceState != null) {
//恢复Presenter数据
TasksFilterType currentFiltering =
(TasksFilterType) savedInstanceState.getSerializable(CURRENT_FILTERING_KEY);
mTasksPresenter.setFiltering(currentFiltering);
}

...

@Override
public void onSaveInstanceState(Bundle outState) {
outState.putSerializable(CURRENT_FILTERING_KEY, mTasksPresenter.getFiltering());
//保存Presenter内的数据
super.onSaveInstanceState(outState);
}

}


}

这样看来,TasksActivity在负责创建TasksFragment和TasksPresenter同时,还负责保存TasksPresenter状态

简单分析M层

M层对应刚才分析的data包,目录结构图如下

类分析

  • Task 实体类
  • TasksDataSource DataSource顶层接口,定义了一些操作数据的抽象方法和回调接口
  • TasksRemoteDataSource 实现了TasksDataSource,进行网络操作的DataSource
  • TasksLocalDataSource 实现了TasksDataSource,进行本地数据操作的DataSource
  • TasksRepository 实现了TasksDataSource,我的理解是M层向上曾提供交付层,持有两种类型的DataSource,根据需求取不同的数据源数据

TasksRepository类分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
public class TasksRepository implements TasksDataSource {
private static TasksRepository INSTANCE = null;

private final TasksDataSource mTasksRemoteDataSource;

private final TasksDataSource mTasksLocalDataSource;

Map<String, Task> mCachedTasks;


private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
@NonNull TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
}

/**
* Returns the single instance of this class, creating it if necessary.
*
* @param tasksRemoteDataSource the backend data source
* @param tasksLocalDataSource the device storage data source
* @return the {@link TasksRepository} instance
*/
public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource,
TasksDataSource tasksLocalDataSource) {
if (INSTANCE == null) {
INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource);
}
return INSTANCE;
}

/**
* Gets tasks from cache, local data source (SQLite) or remote data source, whichever is
* available first.
* <p>
* Note: {@link LoadTasksCallback#onDataNotAvailable()} is fired if all data sources fail to
* get the data.
*/
@Override
public void getTasks(@NonNull final LoadTasksCallback callback) {
checkNotNull(callback);

// Respond immediately with cache if available and not dirty
//如果内存缓存存在且没有过期,返回内存缓存
if (mCachedTasks != null && !mCacheIsDirty) {
callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
return;
}
//获取缓存过期,去网络
if (mCacheIsDirty) {
// If the cache is dirty we need to fetch new data from the network.
getTasksFromRemoteDataSource(callback);
}

//去本地
else {
// Query the local storage if available. If not, query the network.
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是一个单例,构造函数注入 tasksRemoteDataSource和tasksLocalDataSource,分析getTasks方法,是获取任务列表的方法,
该方法获取数据源的优先级是内存->本地->网络,如果内存缓存存在且没有过期,取内存,否则,如果过期,取网络,否则,取本地,如果本地数据不可用再取网络,
从本地获取之后,要覆盖内存缓存,从网络获取之后,要同时覆盖本地和网络。

最后,附上官方架构图

总结:此次分析只是浅尝辄止,具体分析期待下次博客

  • Activity作为Fragment的承载,创建具体的V和P,同时还用来保存P的状态
  • Fragment作为V层来View显示,控制P层数据获取时机
  • 具体的Presenter作为P层,与Repository进行数据交互,对V层提供数据显示
  • 具体的Repository作为M层的门面供P层调用
CATALOG
  1. 1. 分析V层和P层
  2. 2. 简单分析M层