Android开发之最流行的网络框架Retrofit - jsonp及特殊响应数据格式处理

什么是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部分源码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值