Google官方MVP框架源码解读

mvp

最近在看关于MVP框架的封装,于是网上搜了一下,发现了谷歌官方的MVP Demo,发现还是Google的看着顺眼,上图为Demo的框架图。于是就对其分析了一下。Google的MVP版本很多,不过思想都一样,所以就拿当前较火的RxJava那个MVP版本进行分析。

工程结构

首先我们来看一下整个工程里面的包的结构:

工程结构

整个工程结构不难,除了data层是处理数据外,其他都是按功能分模块。

接口类

我们挑其中的一个addedittask模块来看,可以看到里面有个叫AddEditTaskContract的类,这个类是干嘛用的呢? 这就是和普通的MVP结构不同的地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 saveTask(String title, String description);

void populateTask();
}
}

Contract字面意思是契约,这个契约类的作用是把View层和Presenter层的接口包装起来,方便管理,同时也减少了接口文件的数量。BaseView传了个Presenter的泛型进去,我们接下来看看BaseView和BasePresenter:

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

void setPresenter(T presenter);

}
1
2
3
4
5
6
7
public interface BasePresenter {

void subscribe();

void unsubscribe();

}

BaseView中只有一个方法setPresenter,这个方法的作用是获取Presenter对象,方便在View层使用(如在Activity和Fragment中)。看见BasePresenter中的两个方法,是不是就想到了RxJava里的订阅和解除订阅?没错,在这里还真的是这个意思。

下面,就开始介绍它们的实现类了~

接口的实现

Presenter实现类

首先是实现了AddEditTaskContract.Presenter接口的AddEditTaskPresenter类,先看参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@NonNull
private final TasksDataSource mTasksRepository;

@NonNull
private final AddEditTaskContract.View mAddTaskView;

@NonNull
private final BaseSchedulerProvider mSchedulerProvider;

@Nullable
private String mTaskId;

@NonNull
private CompositeSubscription mSubscriptions;

其中TasksDataSource是数据源,AddEditTaskContract.View是View对象,BaseSchedulerProvider是订阅提供者(用于线程切换),CompositeSubscription可以理解为RxJava的订阅管理者,在适当的时候解除订阅防止内存泄漏。

然后看构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
public AddEditTaskPresenter(@Nullable String taskId,
@NonNull TasksDataSource tasksRepository,
@NonNull AddEditTaskContract.View addTaskView,
@NonNull BaseSchedulerProvider schedulerProvider) {
mTaskId = taskId;
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
mAddTaskView = checkNotNull(addTaskView, "addTaskView cannot be null!");
mSchedulerProvider = checkNotNull(schedulerProvider, "schedulerProvider cannot be null!");

mSubscriptions = new CompositeSubscription();
mAddTaskView.setPresenter(this);
}

构造方法传进来了三个主要的参数,同时初始化了CompositeSubscription。注意看,上面BaseView中提到的setPresenter方法在这里用到了,就是获取当前对象,也就是Presenter对象。至于构造方法中传进来的对象怎么来的,我们等下在View层会详细讲。

剩下的就是接口实现的方法了:

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
@Override
public void subscribe() {
if (mTaskId != null) {
populateTask();
}
}

@Override
public void unsubscribe() {
mSubscriptions.clear();
}

/**
* 省略部分代码
*/

@Override
public void populateTask() {
if (mTaskId == null) {
throw new RuntimeException("populateTask() was called but task is new.");
}
Subscription subscription = mTasksRepository
.getTask(mTaskId)
.subscribeOn(mSchedulerProvider.computation())
.observeOn(mSchedulerProvider.ui())
.subscribe(new Observer<Task>() {
@Override
public void onCompleted() {

}

@Override
public void onError(Throwable e) {
if (mAddTaskView.isActive()) {
mAddTaskView.showEmptyTaskError();
}
}

@Override
public void onNext(Task task) {
if (mAddTaskView.isActive()) {
mAddTaskView.setTitle(task.getTitle());
mAddTaskView.setDescription(task.getDescription());
}
}
});

mSubscriptions.add(subscription);
}

我们主要看subscribe、unsubscribe和populateTask方法。在subscribe方法中进行初始化数据,调用了populateTask方法添加订阅和获取数据。unsubscribe方法中进行解除订阅。在populateTask方法中,返回数据的地方调用了View的接口,将数据传给View层。至此,整个Presenter算是讲完啦。我们接下来看到View层,也就是Activity或者Fragment。

View实现类

先看代码,下面的代码我们只看重要的部分,所以省略了很多,想看完整的可以下载Google官方完整的代码:

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
public class AddEditTaskFragment extends Fragment
implements AddEditTaskContract.View {

private AddEditTaskContract.Presenter mPresenter;

/*...*/

@Override
public void onResume() {
super.onResume();
mPresenter.subscribe();
}

@Override
public void onPause() {
super.onPause();
mPresenter.unsubscribe();
}

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

/*...*/

@Override
public void showEmptyTaskError() {
Snackbar.make(mTitle, getString(R.string.empty_task_message), Snackbar.LENGTH_LONG).show();
}

@Override
public void showTasksList() {
getActivity().setResult(Activity.RESULT_OK);
getActivity().finish();
}

@Override
public void setTitle(String title) {
mTitle.setText(title);
}

@Override
public void setDescription(String description) {
mDescription.setText(description);
}

@Override
public boolean isActive() {
return isAdded();
}
}

首先,AddEditTaskFragment实现了AddEditTaskContract.View接口。然后在onResume和onPause这两个关系生命周期的地方进行了和Presenter的绑定(防止内存泄漏),在setPresenter方法中获取Presenter实例。再看下面一堆的View接口中定义的方法,就是得到数据后用来进行界面操作了啦~

Activity

上面说到AddEditTaskPresenter的构造方法中的参数怎么来的,因为是RxJava的MVP版本,就顺带说一下吧。因为整个工程是多Activity+多Fragment的结构,其实是在Activity中创建了Fragment对象,然后添加到布局中的。我们看一下AddEditTaskActivity的代码:

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
public class AddEditTaskActivity extends AppCompatActivity {

public static final int REQUEST_ADD_TASK = 1;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.addtask_act);

/*...*/

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);
} else {
actionBar.setTitle(R.string.add_task);
}

ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
addEditTaskFragment, R.id.contentFrame);
}

// Create the presenter
new AddEditTaskPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
addEditTaskFragment,
Injection.provideSchedulerProvider());
}

/*...*/
}

创建了AddEditTaskFragment对象,然后添加到布局中。最后new了一个AddEditTaskPresenter,这才是重点。到这里,不禁有一个问题,AddEditTaskPresenter构造方法的第二个和第四个参数是哪来的?我们再看Injection类就知道了:

1
2
3
4
5
6
7
8
9
10
11
12
public class Injection {

public static TasksRepository provideTasksRepository(@NonNull Context context) {
checkNotNull(context);
return TasksRepository.getInstance(FakeTasksRemoteDataSource.getInstance(),
TasksLocalDataSource.getInstance(context, provideSchedulerProvider()));
}

public static BaseSchedulerProvider provideSchedulerProvider() {
return SchedulerProvider.getInstance();
}
}

文章都快看完了,都说MVP,Model层在哪里呢?看上面代码中的provideTasksRepository方法,其实这就相当于Model层,获取到数据后传给Presenter,只不过用了一个数据源的名字,功能都一样。provideSchedulerProvider方法是获取一个RxJava的线程调度器:

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 SchedulerProvider implements BaseSchedulerProvider {

@Nullable
private static SchedulerProvider INSTANCE;

// Prevent direct instantiation.
private SchedulerProvider() {
}

public static synchronized SchedulerProvider getInstance() {
if (INSTANCE == null) {
INSTANCE = new SchedulerProvider();
}
return INSTANCE;
}

@Override
@NonNull
public Scheduler computation() {
return Schedulers.computation();
}

@Override
@NonNull
public Scheduler io() {
return Schedulers.io();
}

@Override
@NonNull
public Scheduler ui() {
return AndroidSchedulers.mainThread();
}
}

这个类用了懒汉式获取单例(看源码还是有收获的^_^),实现了BaseSchedulerProvider接口:

1
2
3
4
5
6
7
8
9
10
11
public interface BaseSchedulerProvider {

@NonNull
Scheduler computation();

@NonNull
Scheduler io();

@NonNull
Scheduler ui();
}

一共三个线程,计算线程、IO线程、UI线程,完成了对RxJava线程管理的封装使用。

总结

通过这次解读,收益良多。目前来说,MVP确实是个好框架,而且RxJava便捷的线程切换对于在Model层中进行IO操作和Presenter层的UI操作的支持,两者简直是天生一对。

对于Google官方MVP框架的源码解读到这就结束啦,下一篇将对MVP进行封装一下,让这个框架更加实用,同时简化操作,少写代码。敬请期待~

Share