写之前不得提下接口,接口的设计是这样的
@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))
...