需求
本文不是学术文字讨论(复制粘贴)文,所以不会详细解释什么叫 MVP,MVC,Dagger,Clean 等等。主要以实际项目为基础,简单的实现登录功能。
当代码量越来越大,需要对已有的代码进行整理以及重构,实现解耦.
无论任何的架构模式以及设计模式都是为了代码解耦
研究方向
- Model:
数据层,主要负责网络数据请求与获取,数据库的处理等等数据相关的逻辑处理 - View:
视图层,显示数据,例如 Activity,Fragment,View 等等 UI 载体 - Presenter:
代理层, View 逻辑处理的集合,并且将 Model 获取的数据返回给 View 层
MVP 类型
- MVP 简单版
- MVP-Clean
主要参考实现 Clean Architecture. - MVP-Dagger
Dragger2 的 MVP 实现 - MVP-RxJava
RxJava 的 MVP
实现
MVP 的简单实现
逻辑图
- View
UI 的展示 - BasePresenterImpl
封装了 View 的处理流程,数据回调等等,例如进行网络请求的时候,loading 的显示与隐藏。 - LoginPresenter
登录逻辑处理的地方 - BaseRespository
封装了异常处理,例如网络请求回调的时候,页面消失了,这时候再进行 ui 处理的话就会奔溃。
为了不每个页面都加上判空处理,所以就封装了一层。如不需要可以去掉这一层 - LoginRespository
登录真正执行的地方
关键代码
MainActivity.java
1 | public class MainActivity extends Activity implements IBaseView<String> { |
LoginPresenter.java
1 | public class LoginPresenter extends BasePresenterImpl { |
LoginRepository.java
1 | public class LoginRepository extends BaseRepository implements IBaseRepository { |
完整代码已经上传到github上去了 -> https://github.com/rinfon/mvp.git
MVP-Clean
逻辑图
- Clean 架构图
- 时序图
就像这篇文章Android 官方架构项目之 MVP + Clean 所说,clean 架构比简单版的 MVP 就是多了一层 Domain 层,减轻 Presenter 的负担。
关键代码
在研究官方的Demo中,有些地方还是值得斟酌的
- View 层不通用,例如有些 loading,onSuccess,onError 等通用函数应该抽象到 BaseView 里面去,减少代码量
- 所有的 Task 都在一开始就在界面初始化好了,如果有些功能我没有用到,那么就浪费资源了,应该改成需要用到的时候再初始化
- 在 presenter 初始化的时候,View 层传的参数太多了。例如 View 层所有的 task 都要初始化好传进去。个人认为这是不科学的。但后来想官方这样写的原因是为了区分 View 与 Presenter 的职责(看来我还是太年轻了),View 作为数据的提供者,Presenter 作为数据的接受者。所以才不在 Presenter 层新建数据。既然如此,那只需要 Builder 模式就可以解决参数过多的问题了。
- Presenter 层 callback 重复代码过多,例如
1 | mUseCaseHandler.execute(loginTask, new LoginTask.LoginRequestValues(uid, password), |
每次 excute 都需要提供一个 callback,而每个 callback 仅仅 response 的类型不一样而已。流程还是一样的。所以重复代码还是很多。
原本想在 UserCaseHandler 里面增加 View 的持有,从而在 UserCaseHandler 进行 CallBack 的时候把 closeloading 和 onSucces,onFail 都写好,这样就可以解决问题了。
这样的设计方案有个弊端,View 层入侵了 UserCaseHandler,耦合度又增加了。所以看实际需求来权衡吧
来来来,上代码
MvpCleanActivity.java
1 | public class MvpCleanActivity extends Activity implements LoginContract.View<BaseResponseValues> { |
CleanLoginPresenter.java
1 | public class CleanLoginPresenter implements LoginContract.Presenter { |
LoginTask.java
1 | public class LoginTask extends UseCase<LoginTask.LoginRequestValues, BaseResponseValues> { |
LoginTask 里面跟官方还是不一样的,因为实际需求中,请求的参数和返回的结果结构大多数都是一样的,所以 RequestValues 和 ResponseValues 还是可以封装成公用的。
LoginContract.java
1 | public class LoginContract { |
就像本文所说,View 层可以再封装成 BaseView,把重复的的代码放进去
UserCase
相关的就不放出来了,官方已经封装好了,实际上就是封装了一个线程池,用于异步处理 Task,所以直接使用即可。
完整代码已经上传到 github上去了 -> https://github.com/rinfon/mvp.git
MVP-Dagger
未完待续
MVP-RxJava
RxJava
,之前我一直很抗拒,因为上手感觉很难,另一部分可能自己心燥,静不下来好好研究。但最近看 MVP-clean 的时候有用到,所以也就铁下心来研究了一下。
本来想写篇文章来分享一下的,但后来看到大神们都已经解释的很清楚,连我都能看懂的,我相信你们都能看懂的。
附上链接 ,小水管 RxJava 通过作者的小水管,我相信你们也会喜欢上 RxJava 的。
回到整体
MVP-RxJava,我看了 google 的 demo 之后,发现其实没什么特别的神的地方(应该是没什么出彩的地方),也就是简单版的 MVP 加上 RxJava 而已,所以 demo 就不放出来了。放出关键代码吧
LoginPresenter.Java
1 | mCompositeDisposable.add(Observable.create(new ObservableOnSubscribe<User>() { |
总结
- 任何架构设计都是为了解耦,而解耦随之带来的就是代码的增加以及重复
- 设计的过程中,要注意各层的职责,尽量可以避免入侵
- 在我的 Demo 中,为了处理回调导致的奔溃,我对回调进行了 try catch,这没有问题,但要对详细的异常进行区分,不能所有的异常都吃掉,这是一种不负责的写法(小孩子不要模范啦)有时候,就应该让它 Crash,才知道问题所在。如果非要吃掉所有的异常,也要封装成一个 ErrorCode,对错误信息进行收集。知乎链接,里面有大牛解释的很清楚了。