Retrofit2的一个特色功能,就是通过addConverterFactory()
方法设置一个数据转换器,可以将http请求的响应数据转换成JavaBean。如后台常见的两种返回数据格式:json和xml,分别可以用GsonConverterFactory
和SimpleXmlConverterFactory
进行转换。对应的gradle依赖分别是:
implementation 'com.squareup.retrofit2:converter-gson:2.x.x'
implementation 'com.squareup.retrofit2:converter-simplexml:2.x.x'
SimpleXmlConverterFactory
目前已被声明为Deprecated,推荐的替代方案为JAXB converter(JaxbConverterFactory
),对应的gradle依赖是:
implementation 'com.squareup.retrofit2:converter-jaxb:2.x.x'
但经过实测,该转换器设置在Retrofit中后不能正常工作。
默认情况下,由于Gson(对于JSON)和SimpleXML(对于XML)转换器的行为,一个Retrofit实例不能同时支持这两个转换器。如果这样写:
Retrofit mRetrofit = new Retrofit.Builder()
.baseUrl(URL)
.addConverterFactory(SimpleXmlConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
那么,转换xml格式的返回数据正常,转换json格式的返回数据则会崩溃。
如果这样写:
Retrofit mRetrofit = new Retrofit.Builder()
.baseUrl(URL)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(SimpleXmlConverterFactory.create()
.build();
那么,转换json格式的返回数据正常,转换xml格式的返回数据则会崩溃。
总之就是,只有先添加的那个可以正常使用。
那有没有可以同时正常转换json和xml格式的方案呢?肯定是有的!
给Retrofit要调用的接口方法添加一个注解,说明该请求的响应数据格式。然后自定义一个ConverterFactory,根据注解的属性值决定是使用json转换器还是xml转换器。
自定义注解,比如叫ResponseFormat
:
/**
* 接口返回的数据格式,当前限定取值:{@link #JSON}或{@link #XML}
*/
@Target(METHOD)
@Retention(RUNTIME)
public @interface ResponseFormat {
String JSON = "json";
String XML = "xml";
String value() default "";
}
自定义ConverterFactory,比如叫JsonOrXmlConverterFactory
:
public class JsonOrXmlConverterFactory extends Converter.Factory {
private final Converter.Factory xmlFactory = SimpleXmlConverterFactory.create();
private final Converter.Factory jsonFactory = GsonConverterFactory.create();
public static JsonOrXmlConverterFactory create() {
return new JsonOrXmlConverterFactory();
}
@Nullable
@Override
public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
for (Annotation annotation : annotations) {
if (!(annotation instanceof ResponseFormat)) {
continue;
}
String value = ((ResponseFormat) annotation).value();
if (ResponseFormat.JSON.equals(value)) {
return jsonFactory.responseBodyConverter(type, annotations, retrofit);
} else if (ResponseFormat.XML.equals(value)) {
return xmlFactory.responseBodyConverter(type, annotations, retrofit);
}
}
return null;
}
}
测试代码
interface TestApi {
@GET("testapi/getjson.php/")
@ResponseFormat("json")
Observable getJson();
@GET("testapi/getxml.php/")
@ResponseFormat("xml")
Observable getXml();
}
//……
Retrofit mRetrofit = new Retrofit.Builder()
.baseUrl(URL)
.addConverterFactory(JsonOrXmlConverterFactory.create()
.build();
//……
public void getCode(Consumer consumer) {
TestApi testApi = mRetrofit.create(TestApi.class);
testApi.getJson()
.subscribe(consumer);
}
public void getXml(Consumer consumer) {
TestApi testApi = mRetrofit.create(TestApi.class);
testApi.getXml()
.subscribe(consumer);
}
这个问题虽然有解决方案,但需求场景应该较少。一般后台服务,基于同一个baseUrl的api接口,一般都是统一响应数据格式。只有app中需要从不同的后台服务url请求数据,这样才可能出现多种格式的返回数据,Retrofit本身是支持多baseUrl的。