MVP 思想已经盛行有一段时间了,之前写过几个简单的 MVP Demo,来分离 UI 和逻辑,前段时间研究了 googlesamples/android-architecture,这个库包含了好几种 MVP 例子,我针对 todo-mvp 和 todo-mvp-clean 这两个例子进行了研究,其他几个例子有不同的侧重,但最基本的还是 todo-mvp ,其他只是在这个基础之上做扩充而已,还有一些用到了 Rxjava 的东西,由于项目中没有使用,也就没有详细研究,下面来说说这两个库最基本的用法。
概况
首先要认识到 MVP 是一个思想不是一个框架,不是任何东西都要用它来实现,针对特别简单或者特别复杂的需求或者页面,应根据实际情况来选择,而且要选择使用哪个 MVP 模式作为基本思想。
todo-mvp
Basic Model-View-Presenter architecture,这个是最基本的 MVP 思想,规定了最基本的 View 和 Presenter,使用者需要继承这两个类,来根据实际情况,增加自己的接口关系,这里的 View 和 Presenter 都在一个类中维护 Contract,在官方 Demo 中,Model 数据层的 Repository 在创建 Presenter 的时候就传进来的,而且还是使用的接口,这样也增加了可扩展性,缺点是在调用 M 是一层一层的传递,略显麻烦,不过从代码结构上是清晰的。
这里引用一张官方的图解:
todo-mvp.pngtodo-mvp-clean
Based on todo-mvp, uses concepts from Clean Architecture,依靠的还是还是基本的 MVP todo-mvp,只不过在 Model 层做了优化,引入了 Domain Layer 层, Presenter 将不直接调用数据层,而是使用 usecase 方式。
Presenter 要和 Model 打交道,必须通过 Domain 这一层,UseCase 中提供了最基本的请求和回调参数,以及如何调用的抽象方法,每一个子类继承 UseCase 来实现自己的 UseCase,同时又有一个 UseCaseHandler 工具类来管理这些 UseCase 的执行和回调,UseCase 和 UseCaseHandler 都是提供的基类,可以重复利用,对于 case 比较多的案例还是比较适用的。还有这里面有一些线程池的使用,实现类 UseCaseThreadPoolScheduler,来自定义我们的线程调度,包括使用什么队列,线程池大小等。经实际操作验证,一般的页面无需使用这么复杂的结构,如果针对请求情况比较多的 case,可以使用这个。
Model 这一层在 UseCase 中实现,并且和 todo-mvp 一致,数据层的 Repository 在创建 Presenter 时候也要传入到 UseCase 中。
这里引用一张官方的图解:
todo-mvp-clean.pngtodo-mvp 和 todo-mvp-clean 对比
我们从两张图就能很清晰的看出来 todo-mvp-clean 相对 todo-mvp 新增了 Domain 这一层,使用了 UseCase 方式,将 M 数据层的操作又进一步封装,放入 UseCase 中执行,其他的 V、P 层没有大的变化。
利:
- 代码结构清晰,将每个 case 分离互不干扰,而且请求、回调等进行统一化管理,不在过多的依靠外部提供的接口,增加了灵活性;
- 将 view 和逻辑分离,在整合基线明星页过程中,并没有对view层关注,完全屏蔽掉,直进行 Presenter 分发处理,大大增加了效率;
- Usecase 中有关于线程池的控制以及主线程回调操作;
弊:
- 新增 Usecase 导致代码增多,因为分离的非常细,首先回增加 Usecase 部分的核心代码(这部分可以共用),其次增加了每个具体的 Usecase 类,使得代码整体结构会增加;
- 简单页面不太适合使用 Usecase,建议使用 Base MVP 即可;
其他
-
注意 final 的使用:
private final Contract.View mView; private final UseCaseHandler mUseCaseHandler; private final GetCase1 mGetCase1; public AcitivtyPresenter(UseCaseHandler useCaseHandler, Contract.View statisticsView, GetCase1 getStatistics) { mUseCaseHandler = ActivityUtils.checkNotNull(useCaseHandler, "useCaseHandler cannot be null!"); mView = ActivityUtils.checkNotNull(statisticsView, "StatisticsView cannot be null!"); mGetCase1 = ActivityUtils.checkNotNull(getStatistics, "getStatistics cannot be null!"); mView.setPresenter(this);
也建议规范使用 final ,可以避免自己无意重新赋值或者不创建。
-
关于参数的封装:
public static class RequestValues implements UseCase.RequestValues { } public static class ResponseValue implements UseCase.ResponseValue { private final Statistics mStatistics; public ResponseValue(@NonNull Statistics statistics) { mStatistics = checkNotNull(statistics, "statistics cannot be null!"); } public Statistics getStatistics() { return mStatistics; } }
这种方式提供了一种规范,可扩展、统一封装,这样就可以彻底将数据请求相关的屏蔽掉,如果以后更换请求接口,也不会影响到 UseCase 的使用,更不会影响 View 层了,所以比较推荐这种方式。
-
关于 checkNotNull 方式:
public GetStatistics(@NonNull TasksRepository tasksRepository) { mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!"); }
强烈建议使用 google 的 checkNotNull 这种方式,必须保证不会 null,这个是 Google 的另一库 guava,建议学习下,有很多值得我们学习的地方,比如:字符串操作、I/O等,这里就不展开说了。
- 关于 onError 处理:
public void onFailure() {
getUseCaseCallback().onError();
}
没有错误信息肯定是不行的,官方也建议我们应该包含一些错误信息,例如:异常信息等;
Demo
Demo android-architecture-todo-mvp 是对官方 todo-mvp 和 todo-mvp-clean 精简完的 Demo,只包含了了最基本的 MVP 思想。
参考文章
以下是自己在学习过程中参考的资料
网友评论