登录 立即注册
安币:

安卓巴士 - 安卓开发 - Android开发 - 安卓 - 移动互联网门户

Google官方MVP框架源码解读

2017-1-4 16:52
MrlLee 阅读:507 评论:0 赞:1
Tag:  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进行封装一下,让这个框架更加实用,同时简化操作,少写代码。敬请期待~

转自:http://songning.me/2016/11/03/google-rxjava-mvp/

分享到:
我来说两句
您需要登录后才可以评论 登录 | 立即注册
所有评论(0)

站长推荐

通过邮件订阅最新安卓weekly信息
上一条 /7 下一条

广告投放| Github|申请友链|手机版|站点统计|安卓巴士

返回顶部