3.3 城市列表的设计
很多App都有城市列表这一功能。看似简单,但就像登录功能一样,做好它并不容易。
3.3.1 城市列表数据
一份城市列表的数据包括以下几个字典:
·cityId:城市Id。
·cityName:城市名称。
·pinyin:城市全拼。
·jianpin:城市简拼。
其中,全拼和简拼是用来在App本地做字母表排序和关键字检索的。
我曾经经历过把城市列表数据写死在本地文件的做法,日积月累,就会产生两个问题:
·Android和iOS维护的数据,差异会越来越大。
·一千多个城市,每次从本地加载都要很长时间。
针对问题1的解决办法是,写一个文本分析工具,找出Android和iOS各自维护文件的不同数据。
iOS开发人员喜欢使用plist文件作为数据存储的载体,最好能和Android统一使用一份xml文件,这样便于管理类似城市列表这样的数据。
针对问题2的解决方案是,对于一千多个城市,意味着每次都要解析xml城市数据文件,既然每次读取数据都很慢,那么我们干脆就把序列化过的城市列表直接保存到本地文件,跟随App一起发布。这样,每次读取这个文件时,就直接进行反序列化即可,速度得到很大提升。
把城市列表数据保存在本地,有个很烦的事情,就是每次增加新的城市,都要等下次发版,因为数据是写死在App本地的。于是,我们把城市列表数据做成一个MobileAPI接口,由MobileAPI去后台采集数据,这样数据是最新最准的。
但是这样做的问题是,这个MobileAPI接口返回的数据量会很大,上千笔数据,还包括那么多字段,即使打开了gzip压缩,也会有100k的样子。于是我们又增加了版本号字段version的概念,这个MobileAPI接口的定义和返回的JSON格式是这样的:
1)入参。version,本地存储的城市列表数据对应的版本号。
2)返回值。如果传入参数version和线上最新版本号一致,则返回以下固定格式:
{ "isMatch": false, "version": 1, "cities": [ { }, ] }
如果传入参数version和线上最新版本号不一致,则返回以下格式:
{ "isMatch": false, "version": 1, "cities": [ { "cityId": 1, "cityName": "北京", "pinyin": "beijing", "jianpin": "bj" }, { "cityId": 2, "cityName": "上海", "pinyin": "shanghai", "jianpin": "sh" }, { "cityId": 3, "cityName": "平顶山", "pinyin": "pingdingshan", "jianpin": "pds" } ] }
version这个字段由MobileAPI进行更新,每当有城市数据更新时,version可以立即自增+1,也可以积累到一定数据后自增+1。具体策略由MobileAPI来决定。
基于此,App的策略可以是这样的:
1)本地仍然保存一份线上最新的城市列表数据(序列化后的)以及对应的版本号。我们要求每次发版前做一次城市数据同步的事情。
2)每次进入到城市列表这个页面时,将本地城市列表数据对应的版本号version传入到MobileAPI接口,根据返回的isMatch值来决定是否版本号一致。如果一致,则直接从本地文件中加载城市列表数据;否则,就解析MobileAPI接口返回的数据,在显示列表的同时,记得要把最新的城市列表数据和版本号保存到本地。
3)如果MobileAPI接口没有调用成功,也是直接从本地文件中加载城市列表数据,以确保主流程是畅通的。
4)每次调用MobileAPI时,会获取到大量的数据,一般我们会打开gzip对数据进行压缩,以确保传输的数据量最小。
3.3.2 城市列表数据的增量更新机制
上节中我们谈到,每当有城市数据更新时,version可以立即自增+1。我的问题是,如何判断有城市数据更新?一种解决方案是,在服务器建立一个Timer,每十分钟跑一次,检查10分钟前后的数据是否有改动,如果有,version就自增+1,并返回这些有改动的数据(新增、删除和修改)。这样就保证了10分钟内,从A改成B又改回A,这时候我们认为是没有改动的,版本号不需要自增+1。
那么问题来了,对于1000笔城市数据,每次只改动其中的几笔,返回数据中包括那些没有改动过的数据是没有意义的,是否可以只返回这些改动的数据?
分析1.0和2.0版本的城市列表数据,每笔数据都有cityId和其他一些字段,比如说城市名称、简拼、全拼等。我画了一个表,如图3-2所示,试图展示出1.0和2.0这两个版本的城市数据之间的异同。
图3-2 比较两个版本城市数据间的异同
我来解释一下图3-2,以cityId作为唯一标识,只在1.0中出现的cityId是要删除的数据,只在2.0中出现的cityId是要增加的数据,二者的交集则是cityId相同的数据,这又分为两种情况,所有字段都相同的数据是不变的数据;cityId相同但某个字段不相同,则是修改的数据。
增量更新的数据,就由增、删、改这3部分数据构成。
于是,我们可以重新定义城市列表的JSON格式,在每笔增量数据中增加一个字段type,用来区别是增(c)、删(d)、改(u)中的哪种情况,如下所示:
{ "isMatch": false, "version": 1, "cities": [ { "cityId": 1, "cityName": "北京", "pinyin": "beijing", "jianpin": "bj", "type": "d" }, { "cityId": 2, "cityName": "上海", "pinyin": "shanghai", "jianpin": "sh", "type": "c" }, { "cityId": 3, "cityName": "平顶山", "pinyin": "pingdingshan", "jianpin": "pds", "type": "u" } ] }
客户端在收到上述格式JSON数据后,会根据type值来处理存放在本地的数据。因为不是全量更新,所以处理起来很快。
这种增量更新城市数据的策略,会使得App的逻辑很简单,但是服务器的逻辑很复杂。这样做是划算的,我们要想尽办法确保App的轻量,把复杂的业务逻辑放在后端。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论