post json后台处理数据_Android 优雅地处理后台返回的骚数据

作者:DylanCai
来源:https://juejin.im/post/5dadac2ae51d4524c3745219

前言

Retrofit
是目前主流的网络请求框架,不少用过的小伙伴会遇到这样的问题,绝大部分接口测试都正常,就个别接口尤其是返回失败信息时报了个奇怪的错误信息,而看了自己的代码逻辑也没什么问题。别的接口都是一样的写,却没出现这样的情况,可是后台人员看了也说不关他们的事。刚遇到时会比较懵,有些人不知道什么原因也就无从下手。

问题原因

排查问题也很简单,把信息百度一下,会发现是解析异常。那就先看下后台返回了什么,用 PostMan 请求一下查看返回结果,发现是类似下面这样的:

1{

也可能是这样的:

1{

或者是这样的:

1{

仔细观察后突然恍然大悟,这不是坑爹吗?后台这样返回解析肯定有问题呀,我要将 data
解析成一个对象,而后台返回的是一个空字符串、整形或空数组,肯定解析报错。

嗯,这就是后台的问题,是后台写得不“规范”,所以就跑过去和后台理论让他们改。如果后台是比较好说话,肯配合改还好说。但有些可能是比较“倔强”的性格,可能会说,“这很简单呀,知道是失败状态不解析 data 不就好了?” ,或者说, “为什么 iOS 可以,你这边却不行?你们 Android 有问题就不能自己处理掉吗?” 。如果遇到这样的同事就会比较尴尬。

其实就算后台能根据我们要求改,但也不是长远之计。后台人员变动或自己换个环境可能还是会遇到同样的情况,每次都和后台沟通配合改也麻烦,而且没准就刚好遇到“倔强”不肯改的。

是后台人员写得不规范吗?我个人认为并不是,因为并没有约定俗成的规范要这么写,其实只是后台人员不知道这么返回数据会对 Retrofit
的解析有影响,不知道这么写对 Android
不太友好。后台人员也没有错,我们所觉得的“规范”没人告诉过他呀。可以通过沟通解决问题,不过建议还是自己把问题处理了,一劳永逸。

解决方案

既然是解析报错了,那么在 Gson 解析成对象之前,先验证状态码,判断是错误的情况就抛出异常,这样就不进行后续的 Gson 解析操作去解析
data,也就没问题了。

最先想到的当然是从解析的地方入手,而 Retrofit 能进行 Gson 解析是配置了一个 Gson 转换器。

1retrofit = Retrofit.Builder()

所以我们修改 GsonConverterFactory 不就好了。

自定义 GsonConverterFactory 处理返回结果

试一下会发现并不能直接继承 GsonConverterFactory 重载修改相关方法,因为该类用了 final 修饰。所以只好把
GsonConverterFactory 源码复制出来改,其中关联的两个类 GsonRequestBodyConverter 和
GsonResponseBodyConverter 也要复制修改。下面给出的是 Kotlin 版本的示例。

 1

上面三个类中只需要修改 GsonResponseBodyConverter
的代码,因为是在这个类解析数据。可以在上面有注释的地方加入自己的处理。到底加什么代码,看完后面的内容就知道了。

虽然得到了我们想要的效果,但总感觉并不是很优雅,因为这只是在 gson 解析之前增加一些判断,而为此多写了很多和源码重复的代码。并且存在个问题,这是针对
Retrofit 进行处理的,如果公司用的是自己封装的 OkHttp 请求工具,就没法用这个方案了。

观察一下发现其实只是对一个 ResponseBody 对象进行解析判断状态码,就是说只需要得到个 ResponseBody 对象而已。那么还有什么办法能在
gson 解析之前拿到 ResponseBody 呢?

自定义拦截器处理返回结果

很容易会想到用拦截器,按道理来说是应该是可行的,通过拦截器处理也不局限于使用 Retrofit,用 OkHttp 的也能处理。

想法很美好,但是实际操作起来并没有想象中的简单。刚开始可能会想到用 response.body().string() 读出 json 字符串。

 1

看着好像没问题,但是尝试后发现,状态码是失败的情况确实没毛病,然而状态码是正确的情况却有问题了。

为什么会这样子?有兴趣的可以看下这篇文章《为何 response.body().string() 只能调用一次?》。简单总结一下就是考虑到应用重复读取数据的可能性很小,所以将其设计为一次性流,读取后即关闭并释放资源。我们在拦截器里用通常的
Response 使用方法会把资源释放了,后续解析没有资源了就会有问题。

那该怎么办呢?自己对 Response 的使用又不熟悉,怎么知道该怎么读数据不影响后续的操作。可以参考源码呀,OkHttp
也是用了一些拦截器处理响应数据,它却没有释放掉资源。

这里就不用大家去看源码研究怎么写的了,我直接封装好一个工具类提供大家使用,已经把响应数据的字符串得到了,大家可以直接编写自己的业务代码,拷贝下面的类使用即可。

 1

由于 OkHttp 源码已经用 Kotlin 语言重写了,所以只有个 Kotlin 版本的。但是可能还有很多人还没有用 Kotlin
写项目,所以个人又手动翻译了一个 Java 版本的,方便大家使用,同样拷贝使用即可。

 1

主要是拿到 source 再获得 buffer,然后通过 buffer 去读出字符串。说下其中的一段 gzip
相关的代码,为什么需要有这段代码的处理,自己看源码的话可能会漏掉。这是因为 OkHttp 请求时会添加支持 gzip 压缩的预处理,所以如果响应的数据是gzip 编码的,需要对 gzip 压缩数据解包再去读数据。

好了废话不多说,到底这个工具类怎么用,其实和拦截器一样使用,继承我封装好的 ResponseBodyInterceptor
类,在重写方法里加上自己需要的业务处理代码,body 参数就是我们想要的 json
字符串数据,可以进行解析判断状态码是失败情况并抛出异常。下面给一个简单的解析例子参考,json 结构是文章开头给出的例子,这里假设状态码不是 200
都抛出异常。

 1

然后在 OkHttpClient 中添加该拦截器就可以了。

1

万一后台返回的是更骚的数据呢?

怎么个骚法?有位小伙伴提到的,骚的时候数据还会去 msg 取。好,我们也来帮他处理了。假设返回的数据是下面这样的:

1{

通常 msg 返回的是个字符串,但这次居然是个对象,而且是我们需要得到的数据。我们解析的实体类已经定义了 msg 是字符串,当然不可能因为一个接口把 msg
改成泛型,所以我们需要偷偷地把数据改成我们想要得到的形式。

1{

那么该怎么操作呢?代码比较简单,就不啰嗦了。记得要把该拦截器添加上。

 1

如果用 Java 写的话,可以这样重新生成响应对象。

1

这样就不管后台返回怎样的骚数据,我们根据情况先转成自己想要的数据,再进行解析就没有问题了。

总结

如果只是遇到开头说的 data 类型不定的情况,可以和后台沟通处理,不过还是建议我们也进行处理,加多一层保障,提高 App
的健壮性。如果是后面说的特别骚的返回数据,就建议直接让后台改,实在太骚了不应该纵容。

自定义 GsonConverter 会写不少与源码重复的代码,并不推荐,而且受限于
Retrofit,使用拦截器处理更加的通用。难度在于拦截器该怎么写,所以封装好了工具类供大家使用。

刚开始只是打算分享自己封装好的类,说一下怎么使用来解决问题。不过后来还是花了很多篇幅详细描述了我解决问题的整个心路历程,主要是见过太多人求助这类问题,所以就写详细一点,后续如果还有人问就直接发文章过去,应该能有效解决他的疑惑。另外如果公司用的请求框架即不是
Retrofit 也不是基于 OkHttp 封装的框架的话,通过本文章的解决问题思路应该也能寻找到相应的解决方案。

如果大家有见到问这类问题的小伙伴,也可以直接将本文章分享给他们哟~

推荐阅读

一步步带你读懂 Okhttp 源码Android 点九图机制讲解及在聊天气泡中的应用职场上这四件事,越早知道越好自定义View之双层波纹气泡(xFermode)Android-自定义气泡View,让我们告别.9图面试官:今日头条启动很快,你觉得可能是做了哪些优化?花式实现时间轴,样式由你来定!

ConstraintLayout使用指南

面试官:你有m个鸡蛋,如何用最少的次数测出鸡蛋会在哪一层碎?

Android自定义View-简约风歌词控件

花式实现时间轴,样式由你来定!

面试官:Android 子线程更新UI了解吗?

d7a4033e19612ebb5271058243234d9e.png

扫一扫,欢迎关注我的微信公众号 stormjun94(徐公码字)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android中使用POST请求向后台传递数组可以通过以下步骤实现: 1. 创建一个JSON数组对象,将需要传递的数据添加到其中。例如,以下代码创建了一个包含两个字符串的JSON数组: ``` JSONArray jsonArray = new JSONArray(); jsonArray.put("string1"); jsonArray.put("string2"); ``` 2. 将JSON数组对象转换为字符串。例如,以下代码将上面创建的JSON数组对象转换为字符串: ``` String jsonString = jsonArray.toString(); ``` 3. 创建一个HTTP POST请求对象,并设置请求头和请求体。例如,以下代码创建了一个HTTP POST请求对象: ``` String url = "http://example.com/api"; HttpPost httpPost = new HttpPost(url); httpPost.setHeader("Content-Type", "application/json"); httpPost.setHeader("Accept", "application/json"); StringEntity entity = new StringEntity(jsonString); httpPost.setEntity(entity); ``` 在上面的代码中,设置了请求头为"Content-Type"和"Accept",并将请求体设置为上面转换的JSON数组字符串。 4. 发送HTTP POST请求并处理响应。例如,以下代码发送HTTP POST请求并处理响应: ``` HttpClient httpClient = new DefaultHttpClient(); HttpResponse response = httpClient.execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == HttpStatus.SC_OK) { String responseBody = EntityUtils.toString(response.getEntity()); // 处理响应 } else { // 处理错误 } ``` 在上面的代码中,使用HttpClient发送HTTP POST请求,并检查响应的状态码。如果状态码为200(HttpStatus.SC_OK),则将响应的实体转换为字符串并进行处理。否则,处理错误。 以上是向后台传递数组的一种方法,具体实现可能会根据后台API的设计而有所不同。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值