RxJava2+Retrofit2+RxLifecycle2使用MVP模式构建项目

前言

眼下Retrofit+RxJava搭配的网络请求框架非常流行,本着学习的态度,写了一个相关的demo。写着写着就想朝着搭建一个项目框架的方向走。因而使用了一下MVP模式。git

RxJava 确实挺好用,我的特别喜欢这种“流式”的代码风格,逻辑很清晰,起码提供了一种相对的规范,开发者按照对应的流程写代码,后期的维护和拓展会简单不少。github

MVP模式简单说就是为了解耦,各行各职,阅读代码,拓展功能代价不会那么大(或许有些人认为不必用MVP,直接在activity/fragment中写代码就行了,那只能说你没遇到到过相对大一点的项目,或者没遇到“实习生”写代码,那酸爽,看代码会看得你怀疑人生)服务器

MVP

在使用MVC开发Android应用的时候,原理上网络

  • View:对应于布局文件xml
  • Model:业务逻辑和实体模型
  • Controllor:对应于Activity

可是写代码的时候,你就会发现,好多跟view相关的操做都在activity中实现完成了,致使activity既是Controllor又是View,网上有人称为是MV模式,也所以致使activity的代码量特别大,1000+的代码很常见 
后来,Presenter的出现,将Actvity,xml 视为View层,Model不变,Presenter负责完成View层与Model层的交互。因而MVP是这样的:架构

  • View 对应于Activity,xml 负责View的绘制以及与用户交互
  • Model 依然是业务逻辑和实体模型
  • Presenter 负责完成View于Model间的交互

在网上看了一下MVP的使用demo,挺多人将一个页面须要完成的操做,以及须要用到的控件所有定义到相关的View接口中,示例:框架

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

我的以为,这个有点蛋疼 
1.这样view接口须要的方法太多了,有些实现(clearUserName())能够放在activity中操做,第一不需将控件经过接口传到Presenter中,第二我认为这种算是对View的操做,仍是能够看做View相关的。 
2.咱们很难知道一个界面都要实现些什么方法(若是包括对某个控件内容清空等),可是咱们不难知道一个activity须要实现哪些主要的功能,好比登陆页面就一个登陆功能,或者再加多一个第三方登陆咯。函数

因此我以为View接口中定义一些经常使用的方法,以及一些须要实现的方法就能够了,经过回调内容,把控件赋值,数据展现等仍是放回在activity中操做,presenter只须要将对应的实体或者数据给activity就行了,activity怎么展现,不用管,不关个人事情,作到各行各职。工具

或许有人会说,咦~你这都不是MVP,网上的MVP这些操做要放在presenter中的,这时我上去就给你一巴掌,咱们使用优秀的框架/架构是为了学习它优秀的模式或者编码风格,若是一味的循序渐进照着用,那将毫无心义!同时,咱们只有不断改进,不断推成出新才能使得技术不断进步,若是前人不对MCV提出质疑,就不会有今天的MVP。布局

因此我以为View的接口应该这样 
一个BaseView,定义经常使用的方法,其余页面View接口继承基类单元测试

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这个基类怎么写看项目须要,按照开发者各自需求编写。

如今写一个登陆的LoginView,继承BaseView同时添加特定的接口

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里定义一个showResult(UserBean bean) 将User实体类传给activity,用于展现用户信息。

写到这里的时候的我突然更加坚决我所理解的MVP是对的,解耦嘛 
Presenter:负责获取或者构建UserBean 
Activity:负责展现Presenter给过来的数据

以前看到过有人经过View接口将activity的控件几乎“拷贝”到了presenter中,虽然实现了逻辑处理在Presenter,可是若是Presenter逻辑改动仍是会牵一发动全身,要改动不少 
如今这种方式挺好的,负责构建数据,不参与展现,也方便单元测试。对,就是这样的。

Retrofit2+RxJava2+RxLifecycle2

Retrofit+RxJava确实是一种很不错的搭配,RxJava能够指定运行的线程,在网络请求时,开启线程耗时操做,响应结果时切换为主线程操做UI。很是漂亮,代码风格也赞,我我的称为流式操做,从上到下一步步表明操做的主要逻辑,比起传统的迷之嵌套,迷之缩进好多了。

咱们知道RxJava使用订阅模式,若是没有及时取消订阅,会致使内存泄漏,这个是很是糟糕的行为,固然解决方式也很简单,在对应的生命周期取消订阅就好,不过我仍是怀着好奇之心Github一下,果真已经有人对此做出了贡献RxLifecycle 经过绑定生命周期能够很方便的管理订阅与取消订阅。

Github: https://github.com/trello/RxLifecycle

Retrofit+RxJava的使用仍是挺简单的,不过相对于你们已经用在项目的网络请求框架,它仍是须要进行加工的。好比说错误处理,配合RxLifecycle使用,以及不少人会问的,在MVP中使用的时候怎么取消订阅。

先看下最简单的使用 
接口代码

 
  • 1
  • 2
  • 3
  • 4

使用代码

 
  • 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

代码简介,清晰,构建请求参数,构建被观察者对象,以及传入一个观察者对象实现 
订阅回调onSubscribe 也能够是开始的操做 
成功回调onNext 
失败回调onError 
监听完成onComplete

或许开发者一眼就看出了onError 回调的对象是Throwable 这个不能忍啊,投入使用的框架确定得封装,那就从这里开始

错误处理

在常见的网络请求框架中通常会有两个回调函数

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

定义onError回调函数触发的场景是:1.异常2.错误 
1.异常:请求异常,解析数据出错,网络异常等等 
2.错误:某一次请求逻辑错误,(例如:登陆错误) 
将上述两种状况交给onError回调函数处理 
在请求逻辑成功的时候触发一个onSuccess函数。这样监听者就只须要两个函数,一个失败,一个成功,失败提示给用户,成功负责展现数据,跳转页面等

定义一个异常处理类ExceptionEngine

 
  • 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

异常处理类中,都是常见的错误类型,咱们经过解析Throwable转换成统一的错误类ApiException

 
  • 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

这个类很是简单,一个状态码,一个错误信息,方便咱们开发调试。

不过仔细看代码的同窗会发现,ServerException这个是什么鬼?“服务器返回的错误”?Throwable怎么知道这个错误类型是ServerException

其实这个ServerException是咱们自定义的错误类型,通常咱们开发中都会跟服务器约定一种接口请求返回的数据。好比:

  • int code:表示接口请求状态,0表示成功,-101表示密码错误等等
  • String msg:表示接口请求返回的描述。success,”token过时”等等
  • Object result:成功是返回的数据

那么咱们就能够在解析服务端返回数据的时候,当code!=0,就抛出ServerException

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

OK,到这里咱们常见的错误类型,异常等都处理完了,经过ExceptionEngine转化为统一的错误类型ApiException,在订阅者回调onError(ApiException e)就能够很方便知道错误的状态码以及对应的描述信息。

 
  • 1
  • 2
  • 3
  • 4
  • 5

咱们使用onErrorResumeNext(new HttpResultFunction<>())操做符对Retrofit网络请求抛出的Exception进行处理,咱们定义HttpResultFunction处理Retrofit抛出的Exception,经过ExceptionEngine转化为统一的错误类型ApiException

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这一步是对错误,异常等的处理,若是某一个http请求没有发生异常,或者网络错误,就会走onNext回调。

前面咱们约定,将服务器返回的逻辑错误也归类到onError。因此咱们在.map(new ServerResultFunction())操做符中处理服务器返回的结果是否正确(这里指逻辑正确,即code==0),若是code!=0,就抛出ServerException

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

解析服务器返回结果 HttpResponse,这个类由开发者自行设置,根据实际状况来定义字段,这里我假设后台返回的字段有

String msg; int retCode; Object result;

 
  • 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

这里Result咱们使用Object由于接口时通用的,服务端返回的接口类型也是多样的,多是列表,也多是JSON对象,或者String字符串,因此这里咱们使用Object,在数据解析的时候在转化成为具体的类型

嗯,错误处理搞定了,那就是简单的封装一下Retrofit 和RxJava以及使用RxLifecycle 
RxJava使用订阅模式,那咱们就须要封装一个被订阅者,一个订阅者,以及使用RxLifecycle自动管理订阅的生命周期

构建Api接口类

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

构建Retrofit工具类获取retrofit实例

 
  • 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

构建网络请求(被订阅对象)

 
  • 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

HttpRxObservable咱们构建一个被订阅者Observable而且设置了错误处理,同时添加了生命周期管理。

处理订阅者Observer

 
  • 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

使用网络请求的过程当中咱们确定会遇到取消请求的场景,这里咱们实现一个HttpRequestListener,为每个请求添加惟一的TAG用来标识具体的每个请求,开始请求时保存TAG,请求成功/失败移除标志,同时TAG也用作取消请求的标志。

编写一个测试类,示例如何使用Retrofit

 
  • 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

使用RxLifecycle 管理订阅生命周期activity须要继承RxActivity。开篇也提到MVP的时候,接下来就说说在MVP中如何使用。 
定义一个Presenter基类

 
  • 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
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147

JAVA弱引用,管理View的引用,以及activity的引用,避免强引用致使资源没法释放而形成的内存溢出, 
写代码的时候想到了一个很巧妙的方式:Presenter中传如一个activity,同时实现Activity生命周期监听,在onDestroy中移除View和Activity的引用,我的以为这个很是不错。看官能够客观评价一下优劣。

登陆Presenter

 
  • 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

LoginActivity

 
  • 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

以上是我对MVP的一点理解,以及对RxJava2+Retrofit2+RxLifecycle2进行的封装,因为编写demo的时候是朝着构建一个项目框架走的,因此没有贴完的代码就请移步到个人Github克隆完整版本,尽情的撸吧。 
我的认为这个搭配仍是可行的,而且已经投入都项目中。好产品必定要本身先使用,承认。哈哈哈

Github地址

相关文章
相关标签/搜索