RESTful Android 网络层解决方案(三):API model 与 Business model 分离

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

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

1,API model “碎片化”

当我们的服务端程序是用动态类型语言(例如 PHP)编写的时候,那我们得到的 API 响应就可能会比较杂乱了。

例如根据 id 获取用户信息的 API:

RESTful Android 网络层解决方案(三):API model 与 Business model 分离

这是非好友的情况,如果是好友,情况又还不一样:

RESTful Android 网络层解决方案(三):API model 与 Business model 分离

好友比非好友多了 is_friendfriend_remarkstarred 这三个字段。

而如果获取自己的信息,又还不一样:

RESTful Android 网络层解决方案(三):API model 与 Business model 分离

相比于非好友,多了 phonetokenim_password 这三个字段。

一方面,服务端要践行信息隐藏的原则,不需要的数据就坚决不返回,这就造成即便返回的都是同样的东西(例如用户信息),但返回的字段组合却是多种多样的;另一方面,服务端使用动态类型,无需为每种字段组合创建一个类型,只需要返回时进行组装即可,这就进一步加剧了字段组合“碎片化”的问题。

如何解决这一问题呢?为每种组合创建一个类,还是把所有的字段都揉进一个类?

2,解决方案

上面最后提到的两种办法都有问题,但我们把它们可以结合起来。

首先,对于和 API 打交道的代码,我们把所有字段都装进一个类型, ApiUser 。否则我们就需要定义三个 API 了,而这基本上是不可行的,当我们要获取一个用户的信息时,调用哪个接口,好友还是非好友?我们根本不知道是不是好友!

但紧接着,对于和上层业务打交道的代码,我们要分别定义不同的类型, SelfFriendNonFriend ,绝不包含无用的信息。并且我们把 API 隐藏起来,外部不可访问,对外暴露的接口都要把 ApiUser 转换为相应的 Business model。

3,具体实现

首先,我们把所有的用户信息字段拆分为多个接口,遵循接口隔离。之所以使用接口而不是抽象类,是为了后面进行组合时可以多实现。

3.1,用户信息接口

RESTful Android 网络层解决方案(三):API model 与 Business model 分离

其中 UserInfoModelFriendInfoModel 是由 SqlDelight 生成,用于进行持久化,它们都需要靠 uid 进行查询,所以都包含一个 uid 字段。 RelationshipInfo 用于区分是否是好友, CredentialInfo 则包含自己的信息。

接下来的内容会涉及到 SqlDelight、AutoValue 及其扩展相关的内容,对这些不熟悉的朋友,强烈建议先看一下这篇文章: 完美的安卓 model 层架构(上)

3.2, ApiUser

我们的 ApiUser 要把所有的字段都包含进来,所以要实现上面的所有接口:

RESTful Android 网络层解决方案(三):API model 与 Business model 分离

尽管 UserInfoModelFriendInfoModel 都包含 uid() 接口,但它们组合到一起的时候, ApiUser 只会获得一个 uid() 接口,所以没有问题。这边我们利用 auto-value 实现 immutable,利用 auto-value-gson 实现高效的 Gson 转换。

3.3, NonFriend

RESTful Android 网络层解决方案(三):API model 与 Business model 分离

NonFriend 只包含了基本的用户信息,它实现了 Parcelable ,以便在 Activity/Fragment 之间进行传递。它还提供了一个从 ApiUser 转换的工厂方法。

3.4, Friend

RESTful Android 网络层解决方案(三):API model 与 Business model 分离

Friend 使用组合的方式加入 FriendInfo ,因为 FriendInfo 是需要单独持久化的,所以它需要是一个单独的类型。

3.5, Self

RESTful Android 网络层解决方案(三):API model 与 Business model 分离

至此,API model 和 Business model 都已经定义好了,接下来我们需要把 API 的结果转化为对应的 model。

3.6,API model -> Business model

RESTful Android 网络层解决方案(三):API model 与 Business model 分离

RESTful Android 网络层解决方案(三):API model 与 Business model 分离

API、DB、类型转换的逻辑也并不复杂:

  1. mUserDbAccessor 负责封装数据库访问,我们先尝试从数据库读取缓存。

  2. 如果缓存命中,我们就从 mFriendDbAccessor 中尝试获取好友信息。

  3. 如果没有好友信息,那我们就认为这个用户是 NonFriend。这里我们有一个假设,所有好友都一定会保存好友信息。

  4. 如果有好友信息,那我们就组合出 Friend 返回。

  5. 调用 API 时,获取到的是 ApiUser,我们需要将其转换为 Friend/NonFriend。

  6. 我们利用 concat 把缓存和网络连接起来。

  7. 如果不需要刷新本地缓存,我们直接返回连接结果的第一个即可。

  8. 利用 is_friend ,我们可以确定 ApiUser 是否为 Friend。

  9. 保存用户信息时,如果是好友,我们还需要保存 FriendInfo。

4,单元测试

RESTful Android 网络层解决方案(三):API model 与 Business model 分离

这边我们测例其实并没有做到覆盖所有情形,稍微偷了一下懒,但我们有信心,经过这样的测试,代码已经可靠了。万一真的出了错误,到时候再加上相应的测例,小概率事件到时再说嘛 :)

这里测试代码比较类似,只展示“刷新本地缓存、缓存命中、对方是好友的情形”:

RESTful Android 网络层解决方案(三):API model 与 Business model 分离

RESTful Android 网络层解决方案(三):API model 与 Business model 分离

  1. 我们准备好要返回的 ApiUser、FriendInfo、Friend、NonFriend 信息。

  2. 尽管让 mUserDbAccessor 返回 Friend 语法上没问题,但逻辑上是不会发生的,所以我们还是返回 NonFriend。

  3. 因为我们刷新了本地缓存,而且缓存命中、API 返回数据了,所以我们最终会收到两个 Friend。

  4. 我们对 mFriendDbAccessor 和 mUserDbAccessor 都进行了一次查询操作。

  5. 我们对 API 进行了一次调用。

  6. 我们对 mFriendDbAccessor 和 mUserDbAccessor 都进行了一次保存操作。

5,总结

网络层微架构的内容,本来是只打算写一篇文章的。但是后来发现内容太长,而且没有明确加上单元测试的内容,所以最终把单元测试内容完整加上,拆分为了三篇内容。希望大家能意识到测试代码的重要性:

既然无论手工还是测例,总归是要测试的,那我们何不稍微多花一点工夫,编写单元测试呢?

这套微架构主要包含三部分内容:

而每一部分都包含了尽可能详尽的单元测试,目前看来是最好水平了,已经使出了洪荒之力 :smile:

希望大家喜欢,欢迎留言讨论!

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 REST
反对 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 网络层解决方案(二):空 JSON 和 API Error 解析
    RESTful Android 网络层解决方案(二):空 JSO
    在拆轮子系列:拆 Okio 最后我曾说过会对 Retrofit、OkHttp、Okio 三者进行一个小结,并且整理一套网络层的“微架构”,今天终于得以完成,在这里一起奉送给大家 :) RESTful 安卓网络层解决方案(一):概览与认证实现方案:checkered_flag: RESTful 安卓网络
    09-08 APIJSON
点击排行