写之前不得提下接口,接口的设计是这样的
@ActivityScope
public class RequestParam<T> {
public HeaderEntity header;
public T body;
@Inject
public RequestParam(HeaderEntity header, T body) {
this.header = header;
this.params = body;
}
}
简单说下,接口请求是application/json;,每个请求参数里面包含一个通用的 header里面有一些api、设备和token信息,通常这部分是定义到 Http请求头header里面的,可能我们的接口比较任性...
在header里面我们写一个OkHttp拦截器 然后加header 就可以了,请求体里面拦截器就无能为力了。所以我们用到了请求转换器。
我们在Retrofit的ApiService把 参数类req 定义为 body,通过自定义转换器给body加上header组装成 包含header和body 的完整对象param。
以一个登录接口为例,说明下。
定义在ApiService的方法
@POST(LOGIN_URL)
Observable<ResultWrapper<UserLoginResultDto>> login(@Body UserLoginSearchDto dto);
@ActivityScope
public class UserLoginSearchDto {
public String account;
@NeedMD5
public String password;//md5 加密
@Inject
public UserLoginSearchDto() {}
}
public void login(BaseActivity activity, UserLoginSearchDto dto, Subscriber<UserLoginResultDto> subscriber) {
service.login(dto)
.map(new GetResponseData<>())
//持久化
.doOnNext(user -> App.getApp().getAppComponent().userManager().persist(user))
.compose(activity.bindUntilEvent(ActivityEvent.DESTROY))
.compose(applySchedulers())
.subscribe(subscriber);
}
调用,可以看到,我们的参数UserLoginSearchDto并没有定义header信息。这时自定义转换器就上场了。Retrofit 的代理实现类会通过请求转化器会把我们在ApiService定义的@Body UserLoginSearchDto dto序列化为json。
public class RequestConvertFactory extends Converter.Factory {
public static RequestConvertFactory create() {
return create(new Gson());
}
public static RequestConvertFactory create(Gson gson) {
return new RequestConvertFactory(gson);
}
private final Gson gson;
private RequestConvertFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
@Override
public Converter<?, RequestBody> requestBodyConverter(
Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
//RequestParam 就是包含 header 和 body 的完整参数类
TypeToken<RequestParam<Type>> typeToken = new TypeToken<RequestParam<Type>>() {};
TypeAdapter<RequestParam<Type>> adapter = gson.getAdapter(typeToken);
return new MyGsonRequestBodyConverter<>(gson, adapter);
}
}
public class MyGsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final Gson gson;
private final TypeAdapter<RequestParam<T>> adapter;
MyGsonRequestBodyConverter(Gson gson, TypeAdapter<RequestParam<T>> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override
public RequestBody convert(T value) throws IOException {
Buffer buffer = new Buffer();
Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
JsonWriter jsonWriter = gson.newJsonWriter(writer);
//head头参数
HeaderEntity header = App.getApp().getAppComponent().header();
//实际的参数实体
RequestParam<T> param = new RequestParam<>(header, value);
//时间戳 md5
header.refreshVkey();
UserManager userManager = App.getApp().getAppComponent().userManager();
boolean login = value instanceof UserLoginSearchDto;
header.token = login ? null : userManager.getToken();
header.userId = login ? 0 : userManager.getUserId();
//处理 base64
Class<?> paramClass = value.getClass();
Field[] fields = paramClass.getFields();
Field needMD5 = null;
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
NeedMD5 annotation = field.getAnnotation(NeedMD5.class);
if (annotation != null) {
needMD5 = field;
break;
}
}
if (needMD5 != null) {
try {
needMD5.set(value, EncryptUtil.encryptMD5(String.valueOf(needMD5.get(value))));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
adapter.write(jsonWriter, param);
jsonWriter.close();
//把组装好的完整 param序列化
return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
}
}
别忘了注册自定义转换器RequestConvertFactory.create
Retrofit retrofit = new Retrofit
.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(RequestConvertFactory.create(gson))
.addConverterFactory(GsonConverterFactory.create(gson))
...