2
2
[ ![ Build Status] ( https://travis-ci.org/ysmintor/Retrofit2RxjavaDemo.svg?branch=master )] ( https://travis-ci.org/ysmintor/Retrofit2RxjavaDemo )
3
3
***
4
4
5
- ## English Version
6
- [ please click here to see English version.] ( language/README-EN.md )
7
- To explain how to retrieve data from non restful response to get the right data(I don't have much time to finish English version, please wait for more detail coverage).
8
-
9
- ---
10
5
11
6
## 中文版
12
7
@@ -16,34 +11,31 @@ To explain how to retrieve data from non restful response to get the right data(
16
11
![ rx] ( screenshots/rx.gif )
17
12
![ common] ( screenshots/common.gif )
18
13
19
- 本文主要介绍了使用Retrofit2配合Rxjava[ 这里指Rxjava1, 暂时未适配Rxjava2] 来处理非restful的网络请求结果。一般而言请求的非REST结果如下:
14
+ 本文主要介绍了使用Retrofit2配合Rxjava[ 这里指Rxjava1, 暂时未适配Rxjava2] 来处理非restful的网络请求结果。一般而言请求的非REST结果如下,包括一个code代表结果的状态和msg表示描述,以及data对应的真实的数据。我们的目的就是直接取` data ` 对应的数据,如果是数组那么取的也是数组` list<databean> ` 类型的,如果有错误,那么就会调用onError()方法。更多演示效果请查看video目录下的视频。
15
+
20
16
``` json
21
17
{
22
- "status " : 1 , //请求返回状态码
18
+ "code " : 0 , //请求返回状态码 0表示成功,其它则为对应的错误类型
23
19
"msg" : " 请求成功" , //请求返回状态
24
- "result" : { //返回结果
25
- "npage" : 1 , //当前页
26
- "pageSize" : 10 , //每页信息条数
27
- "total" : 1 , //查询到的信息条数
28
- "data" : [{
20
+ "data" : { //返回结果
29
21
"phone" : " 010-62770334;010-62782051" , //电话
30
22
"website" : " www.tsinghua.edu.cn" , //官网
31
23
"email" :
" [email protected] " ,
//邮箱
32
24
"address" : " 北京市海淀区清华大学" , //地址
33
- "zipcode" : " " , //邮编
25
+ "zipcode" : " 0102770334 " , //邮编
34
26
"name" : " 清华大学" , //学校名称
35
27
"img" : " http://img.jidichong.com/school/3.png" , //学校logo图片
36
28
"parent" : " 教育部" , //隶属部门
37
29
"type" : " 211 985" , //学校类型
38
- "profile" : " " , //简介
30
+ "profile" : " xasd " , //简介
39
31
"info" : " 院士:68人 博士点:198个 硕士点:181个" , //说明
40
32
"city" : " 北京" //所在省市
41
- }]
33
+ }
42
34
}
43
35
}
44
36
```
45
37
46
- 而Retrofit2 请求的结果一般都分为Header 和Body。当然使用使用Rxjava后,则可以实现获取这个result的结果。 当是单个实体的时候,在onNext中就直接得到这个结果,如果是一个数组的时候,则是返回List<Entity > 这种形式。
38
+ 而Retrofit2 请求的结果一般都分为Header 和Body。在获取这些数据后再在Rxjava subscriber 中的onNext()来处理比较麻烦。所以这个demo介绍了如何结果Retrofit2与Rxjava1来处理数据并在成功时得到data字段的数据,服务端返回有错误是进入onError(), 当是单个实体的时候,在onNext中就直接得到这个结果,如果是一个数组的时候,则是返回List<Entity > 这种形式。同时也简化了定义gson实体不用每次都加code, msg这样一层 。
47
39
48
40
49
41
@@ -52,61 +44,76 @@ To explain how to retrieve data from non restful response to get the right data(
52
44
53
45
使用方法:
54
46
55
- 1 . 配置对应的外层实体,例如下面。开发中一般都非标准的REST都是一个数据(百度开放平台的接口就基本都是这种形式),一个状态码和一个消息。其中data的类型是泛型,可以在生成请求的api指定实际返回的类型,如果为空的情况可以使用String。 而code 和message是对应于服务端定义的代码code和返回的错误信息。Demo里有初始值是因为找的接口没有使用这两个字段,但我想模拟这类情形而设置的。如果你的后台后台字段不同,你可以按需要修改这个HttpResult的相应字段。
47
+ 1 . 配置对应的外层实体,例如下面。开发中一般都非标准的REST都是一个数据(百度开放平台的接口就基本都是这种形式),一个状态码和一个消息。其中data的类型是泛型,可以在生成请求的api指定实际返回的类型,如果为空的情况可以使用String。 而code 和message是对应于服务端定义的代码code和返回的错误信息。如果你的后台后台字段不同,你可以按需要修改这个HttpResult的相应字段。
48
+
56
49
```java
57
- {
58
- // code 为返回的状态码, message 为返回的消息, 演示的没有这两个字段,考虑到真实的环境中基本包含就在这里写定值
59
- private int code = 0;
60
- private String message = "OK";
61
-
62
- //用来模仿Data
63
- @SerializedName(value = "subjects")
64
- private T data;
65
- }
50
+ {
51
+ // code 为返回的状态码, message 为返回的消息, 演示的没有这两个字段,考虑到真实的环境中基本包含就在这里写定值
52
+ private int code = 0;
53
+ private String message = "OK";
54
+
55
+ //用来模仿Data
56
+ @SerializedName(value = "subjects")
57
+ private T data;
58
+ }
66
59
```
67
60
68
- 2 . 同Retrofit2一样要定义接口,如下。这里仅仅是有get的接口在demo里,其它的Put, Delete, Query等都是一样的 。
61
+ 2 . 同Retrofit2一样要定义接口,如下。这里仅仅是有GET的接口在demo里,POST, PUT, DELETE, QUERY等都是一样的 。
69
62
70
- `HttpResult`里的类型就是指定泛型data的具体类型 。可以使用String, JSONObject,定义的实体等等。
63
+ `HttpResult`里T的类型就是指定泛型data的具体类型 。可以使用String, JSONObject,定义的实体等等。
71
64
72
65
另外有朋友问题访问参数是JSON对象怎么办 _(Body Paramter JSON Object)_?这其实就是将你的参数设置成一个已经定义的实体,我也给出一个项目中的接口。下面的post就是这种方式。关于请求的REST方式我会在文章后面放出详细的参考,若你不熟悉请参考这些文章。
73
66
```java
74
67
75
- @GET("top250 ")
76
- Observable<HttpResult<List<ContentBean>>> getTopMovie(@Query("start") int start, @Query("count") int count );
68
+ @GET("mock3 ")
69
+ Observable<HttpResult<MockBean>> getMock3( );
77
70
78
- @POST("user/login")
79
- Observable<HttpResult<UserLoginBean>> postLogin(@Body UserLoginRequest request);
71
+ @GET("mock1")
72
+ Observable<HttpResult<List<MockBean>>> getMock1();
73
+
74
+ @GET("mock4")
75
+ Observable<HttpResult<MockBean>> getMock4();
76
+
77
+ @GET("mock2")
78
+ Observable<HttpResult<MockBean>> getMock2();
80
79
```
81
80
82
81
3 . 请求网络。
83
82
84
83
直接调用
85
84
```java
86
- ServiceFactory.movieApi()
87
- .getTopMovie(1, 10)
85
+ public static MockApi mockApi() {
86
+ return ServiceFactory.createService(MockApi.class);
87
+ }
88
88
```
89
89
90
- 使用一个默认的`.compose(new DefaultTransformer<List<ContentBean>>())`可以非常方便地进行转化成了需要的`Observable`。
90
+ 使用一个默认的`.compose(new DefaultTransformer<List<MockBean>>())`可以非常方便地进行转化成了需要的`Observable`。如下代码中那样进行了线程的转换,错误的处理在这个transformer,可以自定义自己的transformer。
91
+ ```java
92
+ return observable
93
+ .subscribeOn(Schedulers.io())
94
+ .observeOn(Schedulers.newThread())
95
+ .compose(ErrorTransformer.<T>getInstance())
96
+ .observeOn(AndroidSchedulers.mainThread());
97
+ ```
91
98
92
99
另外准备了常用的subscriber,包含了网络连接的错误处理,例如非200状态,另外是服务端(业务)错误的处理,默认是将错误编码和错误信息在控制台和手机上输出。
93
- 正确的情况下则必须实现 。
100
+ 提供的`RxSubscriber`和 `CommonScriber`中的`onNext()`必须实现 。
94
101
95
- 建议使用Rxlifecycle防止使用Rxjava内存泄露 。
102
+ 建议使用`RxLifecycle`防止使用`RxJava`内存泄露。其它方面使用同RxJava与Retrofit2结合的使用是相同的,所以得到结果您若有若要对数据进行处理仍然是链式调用 。
96
103
97
104
---
98
105
99
- 错误方面
106
+ #关于错误处理方面介绍
107
+
108
+ 主要使用了RxJava中的` onErrorResumeNext ` ,遇到错误后将错误通过` ExceptionEngine.handleException(throwable) ` 进行处理。
100
109
101
- 定义了服务器错误和连接错误等。主要使用了RxJava中的` onErrorResumeNext ` ,遇到错误后将错误通过` ExceptionEngine.handleException(throwable) ` 进行处理。
102
110
``` java
103
111
@Override
104
112
public Observable<T > call(Observable<HttpResult<T > > responseObservable) {
105
113
return responseObservable. map(new Func1<HttpResult<T > , T > () {
106
114
@Override
107
115
public T call (HttpResult<T > httpResult ) {
108
116
// 通过对返回码进行业务判断决定是返回错误还是正常取数据
109
- // if (httpResult.getCode() != 200) throw new RuntimeException(httpResult.getMessage());
110
117
if (httpResult. getCode() != ErrorType . SUCCESS ) throw new ServerException (httpResult. getMessage(), httpResult. getCode());
111
118
return httpResult. getData();
112
119
}
@@ -120,7 +127,7 @@ To explain how to retrieve data from non restful response to get the right data(
120
127
});
121
128
}
122
129
```
123
- 其中的ApiException
130
+ 其中的ApiException包括code和错误的详情
124
131
``` java
125
132
public class ApiException extends Exception {
126
133
// 异常处理,为速度,不必要设置getter和setter
@@ -134,7 +141,7 @@ public class ApiException extends Exception {
134
141
}
135
142
```
136
143
137
- 重点在于下面这个处理 。你可以再定义自己的业务相关的错误,目前常用的都已经有了,而业相关的错误大部分通过接收的数据直接显示了。若不是直接显示的情况,你完全可以在提供的subscriber里去实现。
144
+ __ 重点在于下面这个处理 。你可以再定义自己的业务相关的错误,目前常用的都已经有了,而业相关的错误大部分通过接收的数据直接显示了。若不是直接显示的情况,你完全可以在提供的subscriber里去实现。__
138
145
``` java
139
146
public class ExceptionEngine {
140
147
// 对应HTTP的状态码
@@ -208,46 +215,85 @@ public class ExceptionEngine {
208
215
}
209
216
```
210
217
218
+ #关于处理服务器在错误时将错误信息直接放在data字段,即data字段在结果成功和失败对应的类型不定。处理思路是自定义GsonConverter,可以查看Demo里的` MockDataActivity ` 去看使用方法,其实就修改Retofit2初始化传入的GsonConverter。关键是对于` CustomGsonResponseBodyConverter ` 的修改。
219
+ ``` java
220
+ @Override
221
+ public T convert(ResponseBody value) throws IOException {
222
+ String response = value. string();
223
+ JsonElement jsonElement = jsonParser. parse(response);
224
+ int parseCode = jsonElement. getAsJsonObject(). get(" code" ). getAsInt();
225
+ //
226
+ if (parseCode != ErrorType . SUCCESS ) {
227
+ value. close();
228
+ String msg = jsonElement. getAsJsonObject(). get(" data" ). getAsString();
229
+ throw new ServerException (msg, parseCode);
230
+ } else {
231
+
232
+ MediaType contentType = value. contentType();
233
+ Charset charset = contentType != null ? contentType. charset(UTF_8 ) : UTF_8 ;
234
+ InputStream inputStream = new ByteArrayInputStream (response. getBytes());
235
+ Reader reader = new InputStreamReader (inputStream, charset);
236
+ JsonReader jsonReader = gson. newJsonReader(reader);
237
+
238
+ try {
239
+ return adapter. read(jsonReader);
240
+ } finally {
241
+ value. close();
242
+ }
243
+ }
244
+ }
245
+ ```
246
+
247
+ 这里先解析code字段再进行判断,所以处理这种服务器返回的话,是需要将上面的` "code" ` 和` "data" ` 替换成你服务端具体的字段。
248
+ -------------------
249
+
250
+
251
+
252
+
211
253
## Update
254
+ ### 2017-03-27
255
+ 更新文档,增加短视频说明,增加错误信息放在data字段的说明。
256
+ ### 2017-03-07
212
257
258
+ 添加处理非REST接口在token失效时或code异常时,错误信息放在data字段的解析办法处理。
213
259
214
- 2017-03-07
215
- 添加处理非REST接口在token失效时或code异常时,错误信息放在data字段的解析办法处理。
216
- >```json
217
- {
218
- code:-1
219
- data:"token失效"
220
- }
260
+ ``` json
261
+ {
262
+ code:-1
263
+ data:"token失效"
264
+ }
265
+ ```
266
+ ``` json
267
+ {
268
+ code:0
269
+ data:{name:"xiaoming", age:23 }
270
+ }
271
+ ```
221
272
222
- {
223
- code:0
224
- data:{name:"xiaoming", age:23}
225
- }
226
- ````
227
- 、
273
+ 针对上面的JSON都要在同一个接口里处理,解决办法都是两次解析的办法,第一次取到code并且判断,不是期望的值进行处理。期望的值可按原路处理。这里采用了修改GsonConverter的办法。
228
274
229
- 针对上面的JSON都要在同一个接口里处理,解决办法都是两次解析的办法,第一次取到code并且判断,不是期望的值进行处理。期望的值可按原路处理。这里采用了修改GsonConverter的办法。
230
- ---
275
+ --------------------
231
276
232
- 2016-12-26
277
+ ### 2016-12-26
233
278
解决了执行onCompleted()之后执行onError()的问题
234
- > ``` java
279
+ ``` java
235
280
if (! isUnsubscribed())
236
281
{
237
282
unsubscribe();
238
283
}
239
- ```
284
+ ```
240
285
241
286
242
- -- -
287
+ -----------------
288
+
243
289
290
+ ### 2016-10-13
244
291
245
- 2016 - 10 - 13
246
292
修正了服务端code没有处理,返回为错误时认为是json实体解析问题。
247
293
248
- > ``` java
294
+ ``` java
249
295
if (httpResult. getCode() != ErrorType . SUCCESS || httpResult. getCode() != ErrorType . SUCCESS )
250
- ```
296
+ ```
251
297
252
298
253
299
## Thanks
0 commit comments