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[ ]会失败:
上网搜了下,并没有找到现成的解决方案,涉及到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[].
这又是什么鬼问题,明明已经自定义了啊!平复了一下心情以后,决定一步一步调试看看...过程就不说了,直接上结果:调试加打印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); } }