什么是jsonp
JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决浏览器的跨域数据访问的问题。返回数据格式如:
jsonp1382016430883([{"id":1,"title":"XXXX"},{"id":2,"title":"YYYYY"}])
或
jsonpCallback({"code":200,"data":[{"id":1,"title":"XXXX"},{"id":2,"title":"YYYYY"}]})
可以看出jsonp和json格式仅仅只是在收尾加了"()"和特殊字符串。我们暂且不管jsonp的概念,今天我要讲的是使用Retrofit请求服务端接口返回的是jsonp格式如何处理问题。
问题引出
我们都知道服务端接口返回数据一般是json/xml/text/html等,而我们现有的数据解析库有pull/sax/gson/fastjson等,用的最多的是gson,但是gson是不支持jsonp格式的,这就需要我们在gson解析返回数据之前做处理,即把非json的数据格式处理成标准的json格式。
解决方案
既然我们要在gson解析响应数据之前处理响应数据,首先会想到Retrofit中的拦截器(Interceptor),当然其实这个部分是属于okhttp的,而我们的项目中用的是Retrofit网络框架自然就离不开okhttp相关的api。 其实有两种方案,一个是利用okhttp的Interceptor这个类在响应之前将数据处理好再交给gson处理,另一个是利用Retrofit的Converter.Factory这个类进行数据转换。
方案一
自定义拦截器(Interceptor),在拦截器里对ResponseBody做处理。这个方法比较挫,但是兼容性很好,比较灵活,适用不同的服务端接口返回数据,比如既有json又有jsonp等。
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class JsonpHandleInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
ResponseBody responseBody = response.body();
MediaType mediaType = responseBody.contentType();
String content = responseBody.string();
int index = content.indexOf("(");
if (/*content.startsWith("jsonp") &&*/ index != -1) {
content = content.substring(index + 1, content.length() - 1);
}
return response.newBuilder()
.body(ResponseBody.create(mediaType, content))
.build();
}
}
复制代码
为okhttp添加拦截器并且设置给Retrofit
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(new JsonpHandleInterceptor())
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://juejin.im")
.client(httpClient)
.build();
复制代码
方案二
自定义Converter.Factory
import android.support.annotation.Nullable;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
public class GsonJsonpConverterFactory extends Converter.Factory {
private final Gson gson;
public static GsonJsonpConverterFactory create() {
return create(new Gson());
}
public static GsonJsonpConverterFactory create(Gson gson) {
if (gson == null) {
throw new NullPointerException("gson == null");
}
return new GsonJsonpConverterFactory(gson);
}
private GsonJsonpConverterFactory(Gson gson) {
this.gson = gson;
}
@Nullable
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonJsonpResponseBodyConverter<>(gson, adapter);
}
}
复制代码
自定义Converter
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import java.io.IOException;
import java.io.Reader;
import okhttp3.ResponseBody;
import retrofit2.Converter;
public final class GsonJsonpResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonJsonpResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override
public T convert(ResponseBody value) throws IOException {
Reader reader = value.charStream();
int item = reader.read();
while (item != '(' && item != -1) {
item = reader.read();
}
JsonReader jsonReader = gson.newJsonReader(reader);
try {
return adapter.read(jsonReader);
} finally {
reader.close();
}
}
}
复制代码
配置Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(httpClient)
.addConverterFactory(GsonJsonpConverterFactory.create())
.build();
复制代码
总结
需要参考GsonConverterFactory部分源码。