RESTful Android 网络层解决方案(二):空 JSON 和 API Error 解析

   2016-09-08 0
核心提示:在拆轮子系列:拆 Okio 最后我曾说过会对 Retrofit、OkHttp、Okio 三者进行一个小结,并且整理一套网络层的“微架构”,今天终于得以完成,在这里一起奉送给大家 :) RESTful 安卓网络层解决方案(一):概览与认证实现方案:checkered_flag: RESTful 安卓网络

在拆轮子系列:拆 Okio 最后我曾说过会对 Retrofit、OkHttp、Okio 三者进行一个小结,并且整理一套网络层的“微架构”,今天终于得以完成,在这里一起奉送给大家 :)

1,JSON 解析需求

JSON 应该是大部分项目 CS 通信的数据格式,相比于简单、调试友好的优势,它的性能不足几乎不足一提,毕竟绝大多数情况下,它都不会成为性能的瓶颈。在 Retrofit + Gson 的方案中,我们有两个问题需要特殊处理。

首先,如果一个 API 请求不需要返回数据,很可能我们的服务器也就不会返回数据(返回空的 response body),而空字符串并不是合法的 JSON,所以 Square 实现的 GsonResponseBodyConverter 会不认账,直接抛出 JSON 解析错误。关于这个问题更多的讨论,可以看一下 Retrofit 的这个 issue:#1554 Handle Empty Body

https://github.com/square/retrofit/issues/1554

其次,很多公司的后端程序都会把 API Error 的 HTTP status code 设置为 200,这样我们就没法利用 OkHttp 的错误处理来解析 API Error 了,我们需要先尝试把响应数据解析为 API Error,如果不是 API Error,再解析为目标类型。

在 Retrofit 1.x 中,Gson 解析是通过设置一个自定义 Converter 来实现的,我们尝试解析为 API Error 的代码自然也在其中,但 Retrofit 在 2.x 中,单独实现了各种常用的 Converter,它们是没法实现我们这种解析需求的。

怎么办呢?其实如果顺着这个思路,答案也会很直接,自己实现一个 Converter 就好了嘛!

但遗憾的是,我在项目重构时没有想到这种方案,而是采用了 Interceptor 的方案,也许是思路被 YLAuthInterceptor 给限制住了。这种方案其实也还说得过去,在拿到网络 response 之后,先拿到数据,再尝试转换为 API Error,如果成功,就抛出这个 API Error,否则返回 response。

但显然让 Converter 来做这件事更加合理,这完全是一件 response 转换的事情,如果说 API error 的响应会带着特殊的 header,那放在 interceptor 层来做就还是合理的。

所以加上这个需求,我们的 converter 需要实现三个功能:JSON 转换、空字符串处理、API Error 检查。

2,解决方案设计

明确了需求之后,有的朋友可能会把这三个功能都放到一起,用一个类来实现,至于 JSON 转换的功能,可以直接把 retrofit-converter-gson 的代码 copy 进来,还省得自己实现。

但这样真的好吗?

首先,copy 别人的代码,就意味着我们也需要对它进行维护,他们发布新版本之后,我们需要把最新的代码再次 copy 进来,这显然是在徒增成本。其次,一个类负责三件事情,一点都不“单一职责”。

那怎么办才好呢?

这里我们现学现卖, Okio 不是很好的践行了“修饰模式”嘛 ,我们这边也可以这么做,动态为 converter 增加功能。这边我们额外实现两个类: EmptyJsonLenientConverterFactoryYLApiErrorAwareConverterFactory ,前者负责处理空 JSON 字符串,后者则用来捕获 API Error。

3,处理空 JSON 字符串

3.1, EmptyJsonLenientConverterFactory

RESTful Android 网络层解决方案(二):空 JSON 和 API Error 解析

RESTful Android 网络层解决方案(二):空 JSON 和 API Error 解析

总的来说还是比较直观的:

  1. 修饰模式要求我们实现同样的接口,并且进行一定程度的委托,我们这边明确就是对 GsonConverterFactory 的功能进行扩充,所以我们的委托类型就直接声明为它。

  2. request body 我们无需特殊处理,直接返回 GsonConverterFactory 创建的 converter。

  3. 我们返回的 converter 可能会被多次使用,所以不要在匿名 converter 实例中创建委托 converter,而是只在外面创建一次。

  4. 尝试把请求转发给 GsonConverterFactory 创建的 converter。

  5. 如果抛出了 EOFException ,则说明遇到了空 JSON 字符串,那我们直接返回 null

3.2,单元测试

同样,我们要编写单元测试,增加我们的信心。

任何事情都不要极端,写完代码之后对着每个 if-else 分支编写测试用例是没必要的,这也会让我们抵触编写测试,因为这样做会让我们觉得测试代码都是重复的“废话”。合理的做法是我们首先就设计一些考察要点,用它们来验证我们的代码是否正确。其实如果不写测试,我们是怎么确保代码正确的呢?还是靠这些“潜在的”测例!所以何不先就把测例准备好呢?何不先就把测试代码写好呢?而这就是 TDD。

我们先看一下测试要点:

RESTful Android 网络层解决方案(二):空 JSON 和 API Error 解析

RESTful Android 网络层解决方案(二):空 JSON 和 API Error 解析

再看 convertNormalJson()

RESTful Android 网络层解决方案(二):空 JSON 和 API Error 解析

测试代码也要保持简洁优雅,否则我们也会对编写测试产生抵触,所以这里我把从 String 创建 ResponseBody 的代码封装了一个函数(1)。

再看 gsonConverterFailOnEmptyJson()

RESTful Android 网络层解决方案(二):空 JSON 和 API Error 解析

这里我们利用 JUnit 的注解来验证测例抛出了 EOFException (1)。

最后我们看看 convertEmptyJson() ,它就非常简单了:

RESTful Android 网络层解决方案(二):空 JSON 和 API Error 解析

4,解析 API Error

4.1, YLApiErrorAwareConverterFactory

RESTful Android 网络层解决方案(二):空 JSON 和 API Error 解析

RESTful Android 网络层解决方案(二):空 JSON 和 API Error 解析

依然比较直观,不过有几点值得一提:

  1. 我们的 YLApiErrorAwareConverterFactory 并不是明确针对哪个具体实现扩充功能的,所以我们把委托声明为接口。

  2. 除了正常的 response body converter,我们还需要一个专门转化为 API Error 的 converter。

  3. 这里我们必须对 ResponseBody 进行 clone,因为 Okio 的流都是只允许读一次的,如果我们直接对传入的参数进行操作,那后面我们尝试解析为正常 body 时就会出错了。

  4. 如果确实是一个 API Error,那我们就抛出它,进入后面的错误处理流程。

4.2,单元测试

同样,先看测例结构:

RESTful Android 网络层解决方案(二):空 JSON 和 API Error 解析

RESTful Android 网络层解决方案(二):空 JSON 和 API Error 解析

测试代码比较简单,我就只贴一下 apiError() 了:

RESTful Android 网络层解决方案(二):空 JSON 和 API Error 解析

这里我们没有利用 JUnit 注解来验证异常的抛出,而是手动编写了 try-catch ,因为我们需要验证 API Error 对象的正确性(1)。

5,小结

好了,JSON 转换中的注意事项也就讲到这里。本文中 converter 对修饰模式的使用算是一大亮点,另外对于单元测试也进行了一定的思考和讨论。在接下来的第三篇中,我将讲讲 model 层中 API 和业务逻辑结合时的一个大问题,欢迎继续阅读 RESTful 安卓网络层解决方案(三):API model 与 Business model 分离

Bonus:拆轮子与 model 层架构推荐

前段时间拆轮子系列的前三篇,分别对 RetrofitOkHttpOkio 源码进行了分析和源码导读,发布之后大家反馈还不错,其中拆 OkHttp 篇成功登上 开发者头条榜首 。没有看过的朋友建议大家可以看一看:

此外,之前整理的安卓 model 层架构,有幸还在 GDG 进行了一次分享,大家反响也还不错,在这里也推荐大家看一看:

  • 完美的安卓 model 层架构(上)

    • http://mp.weixin.qq.com/s?__biz=MzA4MjU5NTY0NA==&mid=2653418653&idx=1&sn=e723665f73936a32f315771d78ac9d6a&scene=1&srcid=0904fROaGjG4263JIpKSmggO#rd

  • 完美的安卓 model 层架构(下)

    • http://mp.weixin.qq.com/s?__biz=MzA4MjU5NTY0NA==&mid=2653418696&idx=1&sn=16a75cebf5b58c97f962934e9883d142&scene=1&srcid=0904eXWlXAflrHDHy7J2litp#rd

  • 08/07 北京 GDG Android Meetup 活动回顾,讲义,照片

    • http://mp.weixin.qq.com/s?__biz=MzA5MDg3MjczMg==&mid=2652003543&idx=1&sn=849c06ac198cbfe9cdcfae90b2a17021&scene=1&srcid=0902QGgAZZKCpZbNNPD66mnu#rd

 
标签: API JSON
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • [译] Android API 指南
    [译] Android API 指南
    众所周知,Android开发者有中文网站了,API 指南一眼看去最左侧的菜单都是中文,然而点进去内容还是很多是英文,并没有全部翻译,我这里整理了API 指南的目录,便于查看,如果之前还没有通读,现在可以好好看一遍。注意,如果标题带有英文,说明官方还没有翻
  • 使用 Android 原生 API 开发设置界面
    使用 Android 原生 API 开发设置界面
    算是 17 年第一篇文章,初衷就是简单介绍下用 xml 配置设置界面,为自定义设置界面 UI 做一个铺垫。场景介绍为了更好的用户体验,现在的大多数的应用都会提供一个设置界面,供用户去设置一些应用属性,比如用户名、昵称等信息的修改,或者还会有一些消息开关
  • 第114期:如果你想要开发一个图片 App,Unsplash API 值得你去玩
    第114期:如果你想要开发一个图片 App,Unsplas
    第114期:如果你想要开发一个图片 App,Unsplash API 值得你去玩头条推荐 1、如果你想要开发一个图片 App,Unsplash API 值得你去玩 Unsplash 是一个免费高质量照片的网站,都是真实的摄影照片,照片分辨率也很大,用来做网页素材已经足够,该照片网站每10天
  • [厉害了Word哥]这些API接口,随便拿出来一个就能装逼、赚钱
    [厉害了Word哥]这些API接口,随便拿出来一个
    来源: http://androidwing.net/index.php MVP盛行,听到的最多的抱怨就是咋要写这么多接口,那么本文作者提供了一个插件,自动生成这些接口的声明。感兴趣的还可以学习该插件的写法,按照自己平时的需求修改,提供开发效率。MVPHelper一款Intellj IDEA 和And
    11-10 APIJava
  • 第112期:如何在没有官方API的情况下写一个第三方客户端
    第112期:如何在没有官方API的情况下写一个第三
    第112期:如何在没有官方API的情况下写一个第三方客户端小公告 Diycode 社区、项目、News、sites 的 API 发布了 大家想怎么玩?Android开发 五分钟带你看懂 Android NestedScrolling 嵌套滑动机制 你了解 NestedScrolling 机制吗? 如何在没有官方API的情况下
  • android 快速开发框架 2.0, 新增异常崩溃友好页面, 开放全套 API 供大家使用
    android 快速开发框架 2.0, 新增异常崩溃友好页
    RapidDevelop-Android快速开发框架 框架持续更新中 这个框架是从平时项目里用的比较多的框架里整合而来 对本项目感兴趣的可以一起研究喜欢的朋友欢迎star 同时也欢迎大家的宝贵意见issues如果大家对MVP模式的开发 网络爬虫以及缓存策略感兴趣的话可以看看我最
  • 学习笔记:Notification API
    学习笔记:Notification API
    Notification API 是浏览器的通知接口,用于在用户的桌面上显示通知信息,桌面电脑和手机都适用。具体的实现形式由浏览器自行部署,对于手机来说,一般显示在顶部的通知栏。如果网页代码调用这个API,浏览器会询问用户是否接受。只有在用户同意的情况下,通知
  • 利用聚合数据API进行Android开发之短信验证码
    利用聚合数据API进行Android开发之短信验证码
    在说Android中的短信验证码这个知识点前,我们首先来了解下聚合数据聚合数据介绍聚合数据是一家国内最大的基础数据API提供商,专业从事互联网数据服务。免费提供从天气查询、空气质量、地图坐标到金融基金、电商比价、违章查询等各个领域的安全、稳定和高效的
  • 使用Android API最佳实践
    使用Android API最佳实践
    写在前面 现在,Android应用程序中集成第三方API已十分流行。应用程序都有自己的网络操作和缓存处理机制,但是大部分比较脆弱,没有针对网络糟糕情况进行优化。感谢 Square lnc这家有创新精神的公司,将信用卡商业交易带到手机上。现在有了一系列高质量开源库
    09-20 APIRetrofit
  • RESTful Android 网络层解决方案(三):API model 与 Business model 分离
    RESTful Android 网络层解决方案(三):API mo
    在拆轮子系列:拆 Okio最后我曾说过会对 Retrofit、OkHttp、Okio 三者进行一个小结,并且整理一套网络层的“微架构”,今天终于得以完成,在这里一起奉送给大家 :) RESTful 安卓网络层解决方案(一):概览与认证实现方案 RESTful 安卓网络层解决方案(二):
    09-11 APIREST
点击排行