Retrofit:打造自己的Converter之byte[]

最近打算着手改造下陈旧的公司项目,首先从网络请求搞起。这个项目最开始用的是一个很小众的网络请求库,叫AQuery,看名字都知道是一个跟JQuery差不多的东西。实际用起来的感受类似volley,有兴趣的可以自行google下看看用法,但是这个毕竟太小众了,而且之前一直怀疑一些卡慢有这个库的一份功劳。本着提升自己顺便解决问题的心态,展开了第一次改造。改造细节就不说了,就是经历了从原生okhttp到大神封装框架的过程。原生最大的问题是response不是在主线程,这样在里面搞一些界面操作就不太方便,后来用了鸿洋大神的框架,省心了许多,不操心什么子线程主线程之类的,然后就这样跑了有半年左右了。

前面说的都是背景。接下来是正题,再次本着提升自己改造项目的目的,准备把网络请求换成Rx+Retrofit,技术出来了很久,也在demo里自己玩过,就琢磨着正式的给项目换上。刚开始改登陆,就被难住了,我举个栗子:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(loginurl)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build();
这个一个正常的构建一个Retrofit对象,问题就出在

.addConverterFactory(GsonConverterFactory.create())
我们的登录接口比较奇葩,返回的是一个byte数组,然后再将其转换为xml,然后再解析...就不追究历史原因了,但是这样就给我留下了一个问题,刚才的标志代码是解析json的,如果不改,直接解析byte[ ]会失败:

Retrofit:打造自己的Converter之byte[]

上网搜了下,并没有找到现成的解决方案,涉及到byte[ ]的都是上传下载文件,也难怪,这么奇葩的接口也是没谁了。变换关键词搜,找到了这个http://www.jianshu.com/p/5598cbe19d6b ,自定义Converter,以及大神的回复 https://github.com/square/retrofit/issues/1733  鼓励小伙伴们自己定义专属Converter 。好吧,那就自己造个*:

public class ToByteConvertFactory extends Converter.Factory{
    private static final MediaType MEDIA_TYPE = MediaType.parse("application/octet-stream");
    private static final String TAG = "ToByteConvertFactory";
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        if(byte[].class.equals(type)){
            return new Converter<ResponseBody, byte[]>() {
                @Override
                public byte[] convert(ResponseBody value) throws IOException {
                    Log.e(TAG, "convert: Converter<ResponseBody, ?>" );
                    return value.bytes();
                }
            };
        }
        return super.responseBodyConverter(type, annotations, retrofit);
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        if(byte[].class.equals(type)){
            return new Converter<byte[], RequestBody>() {
                @Override
                public RequestBody convert(byte[] value) throws IOException {
                    Log.e(TAG, "convert: Converter<?, RequestBody>" );
                    return RequestBody.create(MEDIA_TYPE,value);
                }
            };
        }
        return super.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit);
    }
}
基本是照着自定义String Converter写了一遍。一运行,擦又出问题了:

java.lang.IllegalArgumentException: Unable to create converter for byte[]

以及 Caused by: java.lang.IllegalArgumentException: Could not locate ResponseBody converter for byte[].

Retrofit:打造自己的Converter之byte[]

这又是什么鬼问题,明明已经自定义了啊!平复了一下心情以后,决定一步一步调试看看...过程就不说了,直接上结果:调试加打印log,发现byte[ ].class并不是和type相同,所以进不了条件判断,所以就没法解析了。急中生智,用了个很low的办法,把判断条件改成

if("byte[]".equals(type+""))
不管了,先跑一遍。登录成功!完美

贴一下完整代码

/**
 * Created by xulu on 2017/6/21.
 */
public class ToByteConvertFactory extends Converter.Factory{
    private static final MediaType MEDIA_TYPE = MediaType.parse("application/octet-stream");
    private static final String TAG = "ToByteConvertFactory";
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        Log.e(TAG, "convert: Converter<?, RequestBody>000" +type+"    "+byte[].class);
        if("byte[]".equals(type+"")){
            return new Converter<ResponseBody, byte[]>() {
                @Override
                public byte[] convert(ResponseBody value) throws IOException {
                    Log.e(TAG, "convert: Converter<ResponseBody, ?>" );
                    return value.bytes();
                }
            };
        }
        return super.responseBodyConverter(type, annotations, retrofit);
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        Log.e(TAG, "convert: Converter<?, RequestBody>000" );
        if("byte[]".equals(type+"")){
//        if(byte[].class.equals(type)){
            return new Converter<byte[], RequestBody>() {
                @Override
                public RequestBody convert(byte[] value) throws IOException {
                    Log.e(TAG, "convert: Converter<?, RequestBody>" );
                    return RequestBody.create(MEDIA_TYPE,value);
                }
            };
        }
        return super.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit);
    }
}