Retrofit搭配Rx实践

最近在重构公司的网络层,准备使用最近比较火的组合,采用Retrofit搭配Rx,下面是基础部分一些实践和心得。

1. 初始化

1
2
3
4
5
6
7
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(NetWorkManager.BASE_URL)
.addConverterFactory(LiveStarConverterFactory.create(GsonUtils.getGson()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.callFactory(createOkHttpClient())
.build();
sLiveStarApi = retrofit.create(LiveStarApi.class);

配置基本的url、数据解析和数据请求方式。

2.添加通用参数

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
 static OkHttpClient createOkHttpClient() {
return OkHttpClientProvider.getDefaultOkHttpClient() //这里是默认的client 包括请求超时, 调试抓包
.newBuilder()
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();

request = interceptRequest(request);

return chain.proceed(request);
}
})
.build();
}

static Request interceptRequest(Request request) {
RequestBody requestBody = request.body();
if (requestBody == null) { //get

}else if (requestBody instanceof FormBody) {
FormBody formBody = (FormBody) requestBody;
Map<String, String> params = new HashMap<>();
for (int i = 0; i < formBody.size(); i ++) {
String name = formBody.name(i);
String value = formBody.value(i);
params.put(name, value);
}

appendRequestBody(params);

formBody = generateRequestBody(params);

request = request.newBuilder().post(formBody).build();
}else if (requestBody instanceof MultipartBody) {
MultipartBody multipartBody = (MultipartBody) requestBody;
MultipartBody.Builder builder = new MultipartBody.Builder();
builder.setType(multipartBody.type());
long time = System.currentTimeMillis() / 1000;
String flag = MD5Util.getStringMD5(Contant.USER_IMG_UPLOAD_KEY + time);
builder.addFormDataPart("time", String.valueOf(time));
builder.addFormDataPart("flag", flag);

for (MultipartBody.Part part : multipartBody.parts()) {
builder.addPart(part);
}

request = request.newBuilder().post(builder.build()).build();
}

return request;
}

这里就是通过okhttp的拦截器添加通用参数。 因为涉及到具体业务, 所以不详细贴代码。

3.创建基础Response类

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 LiveStarResponse {

private int code;
private String message;
private boolean state;

public int getCode() {
return code;
}

public void setCode(int code) {
this.code = code;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public boolean isState() {
return state;
}

public void setState(boolean state) {
this.state = state;
}

public boolean isSuccessful() {
return code == ResponseCode.SUCCESS && state;
}
}

4. 返回错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ErrorCheckerTransformer<T extends LiveStarResponse>
implements Observable.Transformer<T, T> {

@Override
public Observable<T> call(Observable<T> observable) {
return observable.map(new Func1<T, T>() {
@Override
public T call(T t) {
if (!t.isSuccessful()) {
throw new RuntimeException(new ErrorResponseException(t));
} else {
return t;
}
}
});
}

public static <T extends LiveStarResponse> ErrorCheckerTransformer<T> create() {
return new ErrorCheckerTransformer<>();
}
}

5. 配置线程

1
2
3
4
5
6
7
8
9
10
11
12
public class SchedulerTransformer<T> implements Observable.Transformer<T, T> {
@Override
public Observable<T> call(Observable<T> observable) {
return observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}

public static <T> SchedulerTransformer<T> create() {
return new SchedulerTransformer<>();
}
}

6. 创建返回错误统一处理方案

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 abstract class DefaultResponseSubscriber<T> extends Subscriber<T> {

private BaseView mBaseView;

public DefaultResponseSubscriber(BaseView baseView) {
mBaseView = baseView;
}

public void onError(Throwable e) {
String hint;
if (e instanceof IOException) { // network is bad
hint = App.getContext().getString(R.string.no_netwrok_hint);
}else if (e instanceof HttpException){ //这里是http code 不是200
HttpException httpException = (HttpException) e;
hint = "Http Exception" + httpException.code() + " " + httpException.message();
}else if (e instanceof RuntimeException
&& e.getCause() instanceof ErrorResponseException) { //这个是服务器返回的数据 不是200
ErrorResponseException errorResponseException = (ErrorResponseException) e.getCause();
if (errorResponseException.getResponse().getCode() == 309) { //token失效。
mBaseView.onAuthError();
return;
}else {
hint = errorResponseException.getResponse().getMessage();
}
}else {
hint = e.getMessage();
}

onFail(e, hint);
}

protected abstract void onFail(Throwable e, String hint);

}

7. 开始请求

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
mCashOutManager.cashOutSwitch()
.compose(ErrorCheckerTransformer.<CheckCashOutResponse>create())
.compose(SchedulerTransformer.<CheckCashOutResponse>create())
.doOnSubscribe(new Action0() {
@Override
public void call() {
mCashOutView.showLoading(true);
}
}).doOnTerminate(new Action0() {
@Override
public void call() {
mCashOutView.showLoading(false);
}
}).subscribe(new DefaultResponseSubscriber<CheckCashOutResponse>(mCashOutView) {
@Override
protected void onFail(Throwable e, String hint) {
mCashOutView.onError(hint);
}

@Override
public void onCompleted() {

}

@Override
public void onNext(CheckCashOutResponse response) {
if (response.isSuccessful() && response.getData() != null
&& !TextUtils.isEmpty(response.getData().getUrl())) {
mCashOutView.onCashOutCheckSuccess(response);
}else {
mCashOutView.onCashOutCheckError(App.getContext().getString(R.string.star_money_tip));
}
}
});

8. 遇到的一些坑

在涉及到MultipartBody的时候GosnConverterFactory默认是MediaType.parse(“application/json; charset=UTF-8”),其实并不是json的格式,会导致请求失败问题。所以我采用自定义RequestBodyConverter对改变基本类型MediaType

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
final class RequestBodyConverter<T> implements Converter<T, RequestBody> {
private static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=UTF-8");
private static final Charset UTF_8 = Charset.forName("UTF-8");

private final Gson gson;
private final TypeAdapter<T> adapter;

RequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}

@Override public RequestBody convert(T value) throws IOException {
if (value instanceof String || value instanceof Long || value instanceof Integer) {
return RequestBody.create(null, String.valueOf(value));
}else {
Buffer buffer = new Buffer();
Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
JsonWriter jsonWriter = gson.newJsonWriter(writer);
adapter.write(jsonWriter, value);
jsonWriter.close();
return RequestBody.create(MEDIA_TYPE_JSON, buffer.readByteString());
}
}
}